@strictly/react-form 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.out/core/mobx/field_adapter.d.ts +7 -6
- package/.out/core/mobx/field_adapter_builder.d.ts +12 -13
- package/.out/core/mobx/field_adapter_builder.js +8 -12
- package/.out/core/mobx/field_adapters_of_values.d.ts +4 -0
- package/.out/core/mobx/flattened_adapters_of_fields.d.ts +2 -2
- package/.out/core/mobx/flattened_list_types_of_type.d.ts +8 -0
- package/.out/core/mobx/form_fields_of_field_adapters.d.ts +8 -0
- package/.out/core/mobx/form_presenter.d.ts +21 -24
- package/.out/core/mobx/form_presenter.js +64 -69
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +13 -0
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.js +11 -0
- package/.out/core/mobx/merge_field_adapters_with_validators.d.ts +11 -0
- package/.out/core/mobx/merge_field_adapters_with_validators.js +45 -0
- package/.out/core/mobx/specs/fixtures.d.ts +7 -0
- package/.out/core/mobx/specs/fixtures.js +20 -0
- package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.js +5 -2
- package/.out/core/mobx/specs/{flattened_list_type_defs_of.tests.js → flattened_list_types_of_types.tests.js} +7 -7
- package/.out/core/mobx/specs/form_presenter.tests.js +162 -60
- package/.out/core/mobx/specs/merge_field_adapters_with_two_way_converter.js +89 -0
- package/.out/core/mobx/specs/merge_field_adapters_with_validators.tests.js +172 -0
- package/.out/core/mobx/types.d.ts +2 -2
- package/.out/field_converters/chain_field_converter.d.ts +3 -3
- package/.out/field_converters/chain_field_converter.js +17 -12
- package/.out/field_converters/identity_converter.d.ts +3 -3
- package/.out/field_converters/identity_converter.js +10 -6
- package/.out/field_converters/integer_to_string_converter.d.ts +5 -4
- package/.out/field_converters/integer_to_string_converter.js +13 -6
- package/.out/field_converters/list_converter.d.ts +2 -2
- package/.out/field_converters/list_converter.js +6 -1
- package/.out/field_converters/maybe_identity_converter.d.ts +3 -3
- package/.out/field_converters/maybe_identity_converter.js +3 -1
- package/.out/field_converters/nullable_to_boolean_converter.d.ts +9 -8
- package/.out/field_converters/nullable_to_boolean_converter.js +13 -7
- package/.out/field_converters/select_value_type_converter.d.ts +20 -15
- package/.out/field_converters/select_value_type_converter.js +29 -14
- package/.out/field_converters/specs/chain_field_converter.tests.d.ts +1 -0
- package/.out/field_converters/specs/chain_field_converter.tests.js +251 -0
- package/.out/field_converters/trimming_string_converter.d.ts +3 -3
- package/.out/field_converters/trimming_string_converter.js +7 -3
- package/.out/field_converters/validating_converter.d.ts +3 -3
- package/.out/field_converters/validating_converter.js +7 -5
- package/.out/index.d.ts +9 -2
- package/.out/index.js +9 -2
- package/.out/mantine/create_checkbox.d.ts +2 -3
- package/.out/mantine/create_checkbox.js +6 -5
- package/.out/mantine/create_pill.js +2 -2
- package/.out/mantine/create_radio.js +1 -1
- package/.out/mantine/create_radio_group.d.ts +2 -3
- package/.out/mantine/create_radio_group.js +4 -3
- package/.out/mantine/create_text_input.d.ts +2 -3
- package/.out/mantine/create_text_input.js +6 -5
- package/.out/mantine/create_value_input.d.ts +2 -3
- package/.out/mantine/create_value_input.js +6 -5
- package/.out/mantine/error_renderer.d.ts +6 -0
- package/.out/mantine/error_renderer.js +5 -0
- package/.out/mantine/hooks.d.ts +9 -13
- package/.out/mantine/hooks.js +10 -15
- package/.out/mantine/specs/checkbox_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/checkbox_hooks.stories.js +33 -6
- package/.out/mantine/specs/list_hooks.stories.js +2 -2
- package/.out/mantine/specs/radio_group_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/radio_group_hooks.stories.js +33 -6
- package/.out/mantine/specs/select_hooks.stories.d.ts +8 -2
- package/.out/mantine/specs/select_hooks.stories.js +45 -8
- package/.out/mantine/specs/text_input_hooks.stories.d.ts +5 -1
- package/.out/mantine/specs/text_input_hooks.stories.js +23 -8
- package/.out/mantine/specs/value_input_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/value_input_hooks.stories.js +49 -15
- package/.out/mantine/types.d.ts +4 -1
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.out/types/error_of_field.d.ts +2 -0
- package/.out/types/error_of_field.js +1 -0
- package/.out/types/field.d.ts +1 -1
- package/.out/types/field_converters.d.ts +17 -10
- package/.out/types/field_converters.js +5 -5
- package/.out/types/flattened_validators_of_fields.d.ts +8 -0
- package/.out/types/flattened_validators_of_fields.js +1 -0
- package/.out/types/merge_validators.d.ts +7 -0
- package/.out/types/merge_validators.js +38 -0
- package/.out/types/specs/flattened_validators_of_fields.tests.d.ts +1 -0
- package/.out/types/specs/flattened_validators_of_fields.tests.js +16 -0
- package/.out/types/specs/merge_validators.tests.d.ts +1 -0
- package/.out/types/specs/merge_validators.tests.js +192 -0
- package/.out/util/partial.d.ts +11 -5
- package/.out/util/partial.js +55 -15
- package/.turbo/turbo-build.log +9 -9
- package/.turbo/turbo-check-types.log +1 -1
- package/.turbo/turbo-release$colon$exports.log +1 -1
- package/README.md +5 -1
- package/core/mobx/field_adapter.ts +15 -7
- package/core/mobx/field_adapter_builder.ts +39 -75
- package/core/mobx/field_adapters_of_values.ts +17 -0
- package/core/mobx/flattened_adapters_of_fields.ts +3 -3
- package/core/mobx/flattened_list_types_of_type.ts +17 -0
- package/core/mobx/form_fields_of_field_adapters.ts +16 -0
- package/core/mobx/form_presenter.ts +117 -104
- package/core/mobx/merge_field_adapters_with_two_way_converter.ts +68 -0
- package/core/mobx/merge_field_adapters_with_validators.ts +99 -0
- package/core/mobx/specs/fixtures.ts +73 -0
- package/core/mobx/specs/flattened_adapters_of_fields.tests.ts +23 -2
- package/core/mobx/specs/flattened_list_types_of_types.tests.ts +35 -0
- package/core/mobx/specs/form_presenter.tests.ts +248 -124
- package/core/mobx/specs/merge_field_adapters_with_two_way_converter.ts +140 -0
- package/core/mobx/specs/merge_field_adapters_with_validators.tests.ts +259 -0
- package/core/mobx/types.ts +3 -3
- package/dist/index.cjs +527 -10412
- package/dist/index.d.cts +153 -111
- package/dist/index.d.ts +153 -111
- package/dist/index.js +527 -10420
- package/field_converters/chain_field_converter.ts +37 -23
- package/field_converters/identity_converter.ts +14 -10
- package/field_converters/integer_to_string_converter.ts +15 -9
- package/field_converters/list_converter.ts +8 -3
- package/field_converters/maybe_identity_converter.ts +7 -4
- package/field_converters/nullable_to_boolean_converter.ts +23 -16
- package/field_converters/select_value_type_converter.ts +86 -26
- package/field_converters/specs/chain_field_converter.tests.ts +302 -0
- package/field_converters/trimming_string_converter.ts +11 -6
- package/field_converters/validating_converter.ts +21 -11
- package/index.ts +9 -2
- package/mantine/create_checkbox.tsx +15 -8
- package/mantine/create_list.tsx +1 -4
- package/mantine/create_pill.tsx +2 -2
- package/mantine/create_radio.tsx +1 -1
- package/mantine/create_radio_group.tsx +8 -6
- package/mantine/create_text_input.tsx +20 -8
- package/mantine/create_value_input.tsx +17 -8
- package/mantine/error_renderer.ts +15 -0
- package/mantine/hooks.tsx +25 -51
- package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +126 -0
- package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +356 -0
- package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +208 -12
- package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +45 -0
- package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +194 -8
- package/mantine/specs/checkbox_hooks.stories.tsx +47 -7
- package/mantine/specs/list_hooks.stories.tsx +2 -2
- package/mantine/specs/radio_group_hooks.stories.tsx +47 -7
- package/mantine/specs/select_hooks.stories.tsx +55 -8
- package/mantine/specs/text_input_hooks.stories.tsx +32 -7
- package/mantine/specs/value_input_hooks.stories.tsx +57 -16
- package/mantine/types.ts +5 -1
- package/package.json +20 -7
- package/tsconfig.json +1 -0
- package/types/error_of_field.ts +3 -0
- package/types/field.ts +1 -1
- package/types/field_converters.ts +21 -10
- package/types/flattened_validators_of_fields.ts +34 -0
- package/types/merge_validators.ts +80 -0
- package/types/specs/error_type_of_field.tests.ts +2 -2
- package/types/specs/flattened_validators_of_fields.tests.ts +93 -0
- package/types/specs/merge_validators.tests.ts +267 -0
- package/util/partial.tsx +200 -16
- package/.out/core/mobx/flattened_list_type_defs_of.d.ts +0 -8
- package/.out/field_validators/minimum_string_length_field_validator.d.ts +0 -2
- package/.out/field_validators/minimum_string_length_field_validator.js +0 -8
- package/.out/types/error_type_of_field.d.ts +0 -2
- package/.out/types/field_validator.d.ts +0 -3
- package/.out/types/flattened_form_fields_of.d.ts +0 -9
- package/.out/types/specs/flattened_form_fields_of.tests.js +0 -13
- package/core/mobx/flattened_list_type_defs_of.ts +0 -17
- package/core/mobx/specs/flattened_list_type_defs_of.tests.ts +0 -35
- package/field_validators/minimum_string_length_field_validator.ts +0 -13
- package/mantine/specs/__snapshots__/check_box_hooks.tests.tsx.snap +0 -227
- package/types/error_type_of_field.ts +0 -3
- package/types/field_validator.ts +0 -7
- package/types/flattened_form_fields_of.ts +0 -16
- package/types/specs/flattened_form_fields_of.tests.ts +0 -43
- /package/.out/core/mobx/{flattened_list_type_defs_of.js → field_adapters_of_values.js} +0 -0
- /package/.out/core/mobx/{specs/flattened_list_type_defs_of.tests.d.ts → flattened_list_types_of_type.js} +0 -0
- /package/.out/{types/error_type_of_field.js → core/mobx/form_fields_of_field_adapters.js} +0 -0
- /package/.out/{types/field_validator.js → core/mobx/specs/flattened_list_types_of_types.tests.d.ts} +0 -0
- /package/.out/{types/flattened_form_fields_of.js → core/mobx/specs/merge_field_adapters_with_two_way_converter.d.ts} +0 -0
- /package/.out/{types/specs/flattened_form_fields_of.tests.d.ts → core/mobx/specs/merge_field_adapters_with_validators.tests.d.ts} +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { mock, mockReset, } from 'vitest-mock-extended';
|
|
2
|
+
export function createMockedAdapter(_original) {
|
|
3
|
+
return mock();
|
|
4
|
+
}
|
|
5
|
+
export function resetMockAdapter({ convert, revert, create, }, mockedAdapter) {
|
|
6
|
+
mockReset(mockedAdapter);
|
|
7
|
+
if (revert) {
|
|
8
|
+
mockedAdapter.revert?.mockImplementation(revert);
|
|
9
|
+
}
|
|
10
|
+
mockedAdapter.convert.mockImplementation(convert);
|
|
11
|
+
mockedAdapter.create.mockImplementation(create);
|
|
12
|
+
}
|
|
13
|
+
export function createMockTwoWayFieldConverter(_original) {
|
|
14
|
+
return mock();
|
|
15
|
+
}
|
|
16
|
+
export function resetMockTwoWayFieldConverter({ convert, revert, }, mockedConverter) {
|
|
17
|
+
mockReset(mockedConverter);
|
|
18
|
+
mockedConverter.convert.mockImplementation(convert);
|
|
19
|
+
mockedConverter.revert.mockImplementation(revert);
|
|
20
|
+
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
const error = Symbol();
|
|
2
|
-
describe('
|
|
2
|
+
describe('FlattenedAdaptersOfFields', function () {
|
|
3
3
|
it('maps the converter types', function () {
|
|
4
4
|
expectTypeOf().toEqualTypeOf();
|
|
5
5
|
});
|
|
6
6
|
it('ignores extraneous types not listed in the fields', function () {
|
|
7
7
|
expectTypeOf().toEqualTypeOf();
|
|
8
8
|
});
|
|
9
|
-
it('handles multiple fields
|
|
9
|
+
it('handles multiple fields', function () {
|
|
10
|
+
expectTypeOf().toEqualTypeOf();
|
|
11
|
+
});
|
|
12
|
+
it('allows synthesized fields', function () {
|
|
10
13
|
expectTypeOf().toEqualTypeOf();
|
|
11
14
|
});
|
|
12
15
|
});
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { list, nullType, numberType, object, record, stringType, union, } from '@strictly/define';
|
|
2
|
-
describe('
|
|
2
|
+
describe('FlattenedListTypesOfType', function () {
|
|
3
3
|
it('filters lists types', function () {
|
|
4
4
|
const listTypeDef = list(numberType);
|
|
5
5
|
const recordTypeDef = record(stringType);
|
|
6
6
|
const objectTypeDef = object();
|
|
7
|
-
const unionTypeDef = union().
|
|
7
|
+
const unionTypeDef = union().or('0', numberType).or('1', nullType);
|
|
8
8
|
const typeDef = object()
|
|
9
|
-
.
|
|
10
|
-
.
|
|
11
|
-
.
|
|
12
|
-
.
|
|
13
|
-
.
|
|
9
|
+
.field('literal', numberType)
|
|
10
|
+
.field('list', listTypeDef)
|
|
11
|
+
.field('record', recordTypeDef)
|
|
12
|
+
.field('object', objectTypeDef)
|
|
13
|
+
.field('union', unionTypeDef);
|
|
14
14
|
expectTypeOf().toEqualTypeOf();
|
|
15
15
|
});
|
|
16
16
|
});
|
|
@@ -4,25 +4,19 @@ import { adapterFromTwoWayConverter, identityAdapter, } from 'core/mobx/field_ad
|
|
|
4
4
|
import { FormModel, FormPresenter, } from 'core/mobx/form_presenter';
|
|
5
5
|
import { IntegerToStringConverter } from 'field_converters/integer_to_string_converter';
|
|
6
6
|
import { NullableToBooleanConverter } from 'field_converters/nullable_to_boolean_converter';
|
|
7
|
+
import { SelectDiscriminatedUnionConverter } from 'field_converters/select_value_type_converter';
|
|
7
8
|
import { prototypingFieldValueFactory } from 'field_value_factories/prototyping_field_value_factory';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
9
|
+
import { UnreliableFieldConversionType, } from 'types/field_converters';
|
|
10
|
+
import { createMockedAdapter, resetMockAdapter, } from './fixtures';
|
|
10
11
|
const IS_NAN_ERROR = 1;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (revert) {
|
|
14
|
-
mockedAdapter.revert?.mockImplementation(revert);
|
|
15
|
-
}
|
|
16
|
-
mockedAdapter.convert.mockImplementation(convert);
|
|
17
|
-
mockedAdapter.create.mockImplementation(create);
|
|
18
|
-
return mockedAdapter;
|
|
19
|
-
}
|
|
12
|
+
const originalIntegerToStringAdapter = adapterFromTwoWayConverter(new IntegerToStringConverter(IS_NAN_ERROR), prototypingFieldValueFactory(0));
|
|
13
|
+
const originalBooleanToBooleanAdapter = identityAdapter(false);
|
|
20
14
|
describe('all', function () {
|
|
21
|
-
const integerToStringAdapter = createMockedAdapter(
|
|
22
|
-
const booleanToBooleanAdapter = createMockedAdapter(
|
|
15
|
+
const integerToStringAdapter = createMockedAdapter(originalIntegerToStringAdapter);
|
|
16
|
+
const booleanToBooleanAdapter = createMockedAdapter(originalBooleanToBooleanAdapter);
|
|
23
17
|
beforeEach(function () {
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
resetMockAdapter(originalIntegerToStringAdapter, integerToStringAdapter);
|
|
19
|
+
resetMockAdapter(originalBooleanToBooleanAdapter, booleanToBooleanAdapter);
|
|
26
20
|
});
|
|
27
21
|
describe('FlattenedTypePathsToConvertersOf', function () {
|
|
28
22
|
describe('record', function () {
|
|
@@ -34,8 +28,8 @@ describe('all', function () {
|
|
|
34
28
|
});
|
|
35
29
|
describe('object', function () {
|
|
36
30
|
const typeDef = object()
|
|
37
|
-
.
|
|
38
|
-
.
|
|
31
|
+
.field('x', stringType)
|
|
32
|
+
.field('y', booleanType);
|
|
39
33
|
let t;
|
|
40
34
|
it('equals expected type', function () {
|
|
41
35
|
expectTypeOf(t).toEqualTypeOf();
|
|
@@ -64,39 +58,66 @@ describe('all', function () {
|
|
|
64
58
|
});
|
|
65
59
|
describe('FormModel', function () {
|
|
66
60
|
describe('literal', function () {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
describe('accessors', function () {
|
|
78
|
-
it('gets the expected value', function () {
|
|
79
|
-
const accessor = expectDefinedAndReturn(model.accessors.$);
|
|
80
|
-
expect(accessor.value).toEqual(originalValue);
|
|
61
|
+
describe('optional', function () {
|
|
62
|
+
const typeDef = numberType;
|
|
63
|
+
const adapters = {
|
|
64
|
+
$: integerToStringAdapter,
|
|
65
|
+
};
|
|
66
|
+
let originalValue;
|
|
67
|
+
let model;
|
|
68
|
+
beforeEach(function () {
|
|
69
|
+
originalValue = 5;
|
|
70
|
+
model = new FormModel(typeDef, originalValue, adapters);
|
|
81
71
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
72
|
+
describe('accessors', function () {
|
|
73
|
+
it('gets the expected value', function () {
|
|
74
|
+
const accessor = expectDefinedAndReturn(model.accessors.$);
|
|
75
|
+
expect(accessor.value).toEqual(originalValue);
|
|
76
|
+
});
|
|
77
|
+
it('sets the underlying value', function () {
|
|
78
|
+
const newValue = 1;
|
|
79
|
+
const accessor = expectDefinedAndReturn(model.accessors.$);
|
|
80
|
+
accessor.set(newValue);
|
|
81
|
+
expect(model.value).toEqual(newValue);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe('fields', function () {
|
|
85
|
+
it('equals expected value', function () {
|
|
86
|
+
expect(model.fields).toEqual(expect.objectContaining({
|
|
87
|
+
$: expect.objectContaining({
|
|
88
|
+
value: '5',
|
|
89
|
+
}),
|
|
90
|
+
}));
|
|
91
|
+
});
|
|
92
|
+
it('has the expected keys', function () {
|
|
93
|
+
expect(Object.keys(model.fields)).toEqual(['$']);
|
|
94
|
+
});
|
|
87
95
|
});
|
|
88
96
|
});
|
|
89
|
-
describe('
|
|
90
|
-
|
|
97
|
+
describe('required', function () {
|
|
98
|
+
const typeDef = numberType;
|
|
99
|
+
const adapters = {
|
|
100
|
+
$: integerToStringAdapter,
|
|
101
|
+
};
|
|
102
|
+
let originalValue;
|
|
103
|
+
let model;
|
|
104
|
+
beforeEach(function () {
|
|
105
|
+
integerToStringAdapter.convert.mockReturnValue({
|
|
106
|
+
value: 'x',
|
|
107
|
+
required: true,
|
|
108
|
+
readonly: false,
|
|
109
|
+
});
|
|
110
|
+
originalValue = 5;
|
|
111
|
+
model = new FormModel(typeDef, originalValue, adapters);
|
|
112
|
+
});
|
|
113
|
+
it('reports required status', function () {
|
|
91
114
|
expect(model.fields).toEqual(expect.objectContaining({
|
|
92
115
|
$: expect.objectContaining({
|
|
93
|
-
value: '
|
|
116
|
+
value: 'x',
|
|
117
|
+
required: true,
|
|
94
118
|
}),
|
|
95
119
|
}));
|
|
96
120
|
});
|
|
97
|
-
it('has the expected keys', function () {
|
|
98
|
-
expect(Object.keys(model.fields)).toEqual(['$']);
|
|
99
|
-
});
|
|
100
121
|
});
|
|
101
122
|
});
|
|
102
123
|
describe('list', function () {
|
|
@@ -142,6 +163,24 @@ describe('all', function () {
|
|
|
142
163
|
]);
|
|
143
164
|
});
|
|
144
165
|
});
|
|
166
|
+
describe('fields', function () {
|
|
167
|
+
it('equals the expected value', function () {
|
|
168
|
+
expect(model.fields).toEqual(expect.objectContaining({
|
|
169
|
+
'$.0': expect.objectContaining({
|
|
170
|
+
value: '1',
|
|
171
|
+
required: false,
|
|
172
|
+
}),
|
|
173
|
+
'$.1': expect.objectContaining({
|
|
174
|
+
value: '4',
|
|
175
|
+
required: false,
|
|
176
|
+
}),
|
|
177
|
+
'$.2': expect.objectContaining({
|
|
178
|
+
value: '17',
|
|
179
|
+
required: false,
|
|
180
|
+
}),
|
|
181
|
+
}));
|
|
182
|
+
});
|
|
183
|
+
});
|
|
145
184
|
});
|
|
146
185
|
describe('record', function () {
|
|
147
186
|
const typeDef = record(numberType);
|
|
@@ -194,8 +233,8 @@ describe('all', function () {
|
|
|
194
233
|
});
|
|
195
234
|
describe('object', function () {
|
|
196
235
|
const typeDef = object()
|
|
197
|
-
.
|
|
198
|
-
.
|
|
236
|
+
.field('a', numberType)
|
|
237
|
+
.field('b', booleanType);
|
|
199
238
|
const converters = {
|
|
200
239
|
'$.a': integerToStringAdapter,
|
|
201
240
|
'$.b': booleanToBooleanAdapter,
|
|
@@ -268,7 +307,6 @@ describe('all', function () {
|
|
|
268
307
|
expect(model.fields).toEqual(expect.objectContaining({
|
|
269
308
|
$: expect.objectContaining({
|
|
270
309
|
value: '1',
|
|
271
|
-
// eslint-disable-next-line no-undefined
|
|
272
310
|
error: undefined,
|
|
273
311
|
}),
|
|
274
312
|
}));
|
|
@@ -296,7 +334,7 @@ describe('all', function () {
|
|
|
296
334
|
const errorCode = 65;
|
|
297
335
|
beforeEach(function () {
|
|
298
336
|
integerToStringAdapter.revert?.mockReturnValueOnce({
|
|
299
|
-
type:
|
|
337
|
+
type: UnreliableFieldConversionType.Failure,
|
|
300
338
|
error: errorCode,
|
|
301
339
|
value: [newValue],
|
|
302
340
|
});
|
|
@@ -310,7 +348,7 @@ describe('all', function () {
|
|
|
310
348
|
$: expect.objectContaining({
|
|
311
349
|
value: '-1',
|
|
312
350
|
error: errorCode,
|
|
313
|
-
|
|
351
|
+
readonly: false,
|
|
314
352
|
}),
|
|
315
353
|
});
|
|
316
354
|
});
|
|
@@ -337,7 +375,6 @@ describe('all', function () {
|
|
|
337
375
|
expect(model.fields).toEqual(expect.objectContaining({
|
|
338
376
|
$: expect.objectContaining({
|
|
339
377
|
value: newValue,
|
|
340
|
-
// eslint-disable-next-line no-undefined
|
|
341
378
|
error: undefined,
|
|
342
379
|
}),
|
|
343
380
|
}));
|
|
@@ -376,7 +413,6 @@ describe('all', function () {
|
|
|
376
413
|
expect(model.fields).toEqual(expect.objectContaining({
|
|
377
414
|
'$.0': expect.objectContaining({
|
|
378
415
|
value: '100',
|
|
379
|
-
// eslint-disable-next-line no-undefined
|
|
380
416
|
error: undefined,
|
|
381
417
|
}),
|
|
382
418
|
}));
|
|
@@ -413,7 +449,6 @@ describe('all', function () {
|
|
|
413
449
|
expect(model.fields).toEqual(expect.objectContaining({
|
|
414
450
|
'$.0': expect.objectContaining({
|
|
415
451
|
value: newValue,
|
|
416
|
-
// eslint-disable-next-line no-undefined
|
|
417
452
|
error: undefined,
|
|
418
453
|
}),
|
|
419
454
|
}));
|
|
@@ -434,7 +469,6 @@ describe('all', function () {
|
|
|
434
469
|
}),
|
|
435
470
|
'$.1': expect.objectContaining({
|
|
436
471
|
value: '2',
|
|
437
|
-
// eslint-disable-next-line no-undefined
|
|
438
472
|
error: undefined,
|
|
439
473
|
}),
|
|
440
474
|
'$.2': expect.objectContaining({
|
|
@@ -458,7 +492,7 @@ describe('all', function () {
|
|
|
458
492
|
integerToStringAdapter.revert.mockImplementationOnce(function (_value, _path, context) {
|
|
459
493
|
contextCopy = [...context];
|
|
460
494
|
return {
|
|
461
|
-
type:
|
|
495
|
+
type: UnreliableFieldConversionType.Success,
|
|
462
496
|
value: 1,
|
|
463
497
|
};
|
|
464
498
|
});
|
|
@@ -517,7 +551,6 @@ describe('all', function () {
|
|
|
517
551
|
it.each([
|
|
518
552
|
[
|
|
519
553
|
'$.0',
|
|
520
|
-
// eslint-disable-next-line no-undefined
|
|
521
554
|
undefined,
|
|
522
555
|
],
|
|
523
556
|
[
|
|
@@ -624,14 +657,14 @@ describe('all', function () {
|
|
|
624
657
|
describe('union', function () {
|
|
625
658
|
describe('non-discriminated', function () {
|
|
626
659
|
const listOfNumbersTypeDef = list(numberType);
|
|
627
|
-
const
|
|
628
|
-
.
|
|
629
|
-
.
|
|
660
|
+
const type = union()
|
|
661
|
+
.or('null', nullType)
|
|
662
|
+
.or('0', listOfNumbersTypeDef);
|
|
630
663
|
const adapters = {
|
|
631
|
-
$: adapterFromTwoWayConverter(new NullableToBooleanConverter(
|
|
664
|
+
$: adapterFromTwoWayConverter(new NullableToBooleanConverter(type, [1], null)),
|
|
632
665
|
'$.*': integerToStringAdapter,
|
|
633
666
|
};
|
|
634
|
-
const presenter = new FormPresenter(
|
|
667
|
+
const presenter = new FormPresenter(type, adapters);
|
|
635
668
|
let originalValue;
|
|
636
669
|
let model;
|
|
637
670
|
beforeEach(function () {
|
|
@@ -641,8 +674,7 @@ describe('all', function () {
|
|
|
641
674
|
it('has the expected fields', function () {
|
|
642
675
|
expect(model.fields).toEqual({
|
|
643
676
|
$: {
|
|
644
|
-
|
|
645
|
-
// eslint-disable-next-line no-undefined
|
|
677
|
+
readonly: false,
|
|
646
678
|
error: undefined,
|
|
647
679
|
value: false,
|
|
648
680
|
required: false,
|
|
@@ -660,6 +692,76 @@ describe('all', function () {
|
|
|
660
692
|
});
|
|
661
693
|
});
|
|
662
694
|
});
|
|
695
|
+
describe('discriminated', function () {
|
|
696
|
+
const struct1 = object().field('a', numberType);
|
|
697
|
+
const struct2 = object().field('b', booleanType);
|
|
698
|
+
const type = union('d')
|
|
699
|
+
.or('x', struct1)
|
|
700
|
+
.or('y', struct2);
|
|
701
|
+
const adapters = {
|
|
702
|
+
$: adapterFromTwoWayConverter(new SelectDiscriminatedUnionConverter(type, {
|
|
703
|
+
x: {
|
|
704
|
+
d: 'x',
|
|
705
|
+
a: 0,
|
|
706
|
+
},
|
|
707
|
+
y: {
|
|
708
|
+
d: 'y',
|
|
709
|
+
b: false,
|
|
710
|
+
},
|
|
711
|
+
}, 'x', true)).narrow,
|
|
712
|
+
'$.x:a': identityAdapter(0).narrow,
|
|
713
|
+
'$.y:b': identityAdapter(false).narrow,
|
|
714
|
+
};
|
|
715
|
+
const presenter = new FormPresenter(type, adapters);
|
|
716
|
+
describe('isValuePathActive', function () {
|
|
717
|
+
describe('discriminator x', function () {
|
|
718
|
+
const model = presenter.createModel({
|
|
719
|
+
d: 'x',
|
|
720
|
+
a: 1,
|
|
721
|
+
});
|
|
722
|
+
it.each([
|
|
723
|
+
[
|
|
724
|
+
'$',
|
|
725
|
+
true,
|
|
726
|
+
],
|
|
727
|
+
[
|
|
728
|
+
'$.x:a',
|
|
729
|
+
true,
|
|
730
|
+
],
|
|
731
|
+
[
|
|
732
|
+
'$.y:b',
|
|
733
|
+
false,
|
|
734
|
+
],
|
|
735
|
+
])('value path %s is active %s', function (path, expected) {
|
|
736
|
+
const isValid = presenter.isValuePathActive(model, path);
|
|
737
|
+
expect(isValid).toBe(expected);
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
describe('discriminator y', function () {
|
|
741
|
+
const model = presenter.createModel({
|
|
742
|
+
d: 'y',
|
|
743
|
+
b: false,
|
|
744
|
+
});
|
|
745
|
+
it.each([
|
|
746
|
+
[
|
|
747
|
+
'$',
|
|
748
|
+
true,
|
|
749
|
+
],
|
|
750
|
+
[
|
|
751
|
+
'$.x:a',
|
|
752
|
+
false,
|
|
753
|
+
],
|
|
754
|
+
[
|
|
755
|
+
'$.y:b',
|
|
756
|
+
true,
|
|
757
|
+
],
|
|
758
|
+
])('value path %s is active %s', function (path, expected) {
|
|
759
|
+
const isValid = presenter.isValuePathActive(model, path);
|
|
760
|
+
expect(isValid).toBe(expected);
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
});
|
|
764
|
+
});
|
|
663
765
|
});
|
|
664
766
|
describe('fake', function () {
|
|
665
767
|
const typeDef = numberType;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { identityAdapter } from 'core/mobx/field_adapter_builder';
|
|
2
|
+
import { mergeFieldAdaptersWithTwoWayConverter, } from 'core/mobx/merge_field_adapters_with_two_way_converter';
|
|
3
|
+
import { annotatedIdentityConverter, unreliableIdentityConverter, } from 'field_converters/identity_converter';
|
|
4
|
+
import { UnreliableFieldConversionType, } from 'types/field_converters';
|
|
5
|
+
import { createMockedAdapter, createMockTwoWayFieldConverter, resetMockAdapter, resetMockTwoWayFieldConverter, } from './fixtures';
|
|
6
|
+
const error1 = Symbol();
|
|
7
|
+
const error2 = Symbol();
|
|
8
|
+
const error3 = Symbol();
|
|
9
|
+
const error4 = Symbol();
|
|
10
|
+
const context = Symbol();
|
|
11
|
+
describe('MergedOfFieldAdapterWithTwoWayConverter', function () {
|
|
12
|
+
let m;
|
|
13
|
+
it('merges the errors', function () {
|
|
14
|
+
expectTypeOf().toEqualTypeOf(m);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
const originalIntegerAdapter = identityAdapter(0);
|
|
18
|
+
const originalBooleanAdapter = identityAdapter(false, true);
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
+
const originalConverter = {
|
|
21
|
+
convert: annotatedIdentityConverter(),
|
|
22
|
+
revert: unreliableIdentityConverter(),
|
|
23
|
+
};
|
|
24
|
+
describe('mergeFieldAdaptersWithTwoWayConverter', function () {
|
|
25
|
+
const integerAdapter = createMockedAdapter(originalIntegerAdapter);
|
|
26
|
+
const booleanAdapter = createMockedAdapter(originalBooleanAdapter);
|
|
27
|
+
beforeEach(function () {
|
|
28
|
+
resetMockAdapter(originalIntegerAdapter, integerAdapter);
|
|
29
|
+
resetMockAdapter(originalBooleanAdapter, booleanAdapter);
|
|
30
|
+
});
|
|
31
|
+
describe('two entries', function () {
|
|
32
|
+
const fieldAdapters = {
|
|
33
|
+
integerAdapter,
|
|
34
|
+
booleanAdapter,
|
|
35
|
+
};
|
|
36
|
+
const converter = createMockTwoWayFieldConverter(originalConverter);
|
|
37
|
+
beforeEach(function () {
|
|
38
|
+
resetMockTwoWayFieldConverter(originalConverter, converter);
|
|
39
|
+
});
|
|
40
|
+
const merged = mergeFieldAdaptersWithTwoWayConverter(fieldAdapters, converter);
|
|
41
|
+
describe('convert', function () {
|
|
42
|
+
let result;
|
|
43
|
+
describe('success', function () {
|
|
44
|
+
// note don't really need to exercise this too extensively since most of
|
|
45
|
+
// the work is done in chainXFieldAdapter
|
|
46
|
+
beforeEach(function () {
|
|
47
|
+
result = merged.booleanAdapter.convert(true, 'booleanAdapter', context);
|
|
48
|
+
});
|
|
49
|
+
it('returns the same value on convert', function () {
|
|
50
|
+
expect(result).toEqual(expect.objectContaining({
|
|
51
|
+
value: true,
|
|
52
|
+
}));
|
|
53
|
+
});
|
|
54
|
+
it('calls the mocked converter', function () {
|
|
55
|
+
expect(converter.convert).toHaveBeenCalledOnce();
|
|
56
|
+
expect(converter.convert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
|
|
57
|
+
});
|
|
58
|
+
it('calls the mocked adapter', function () {
|
|
59
|
+
expect(booleanAdapter.convert).toHaveBeenCalledOnce();
|
|
60
|
+
expect(booleanAdapter.convert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe('revert', function () {
|
|
65
|
+
let result;
|
|
66
|
+
describe('success', function () {
|
|
67
|
+
// note don't really need to exercise this too extensively since most of
|
|
68
|
+
// the work is done in chainXFieldAdapter
|
|
69
|
+
beforeEach(function () {
|
|
70
|
+
result = merged.booleanAdapter.revert(true, 'booleanAdapter', context);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
it('returns the same value on revert', function () {
|
|
74
|
+
expect(result).toEqual(expect.objectContaining({
|
|
75
|
+
value: true,
|
|
76
|
+
type: UnreliableFieldConversionType.Success,
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
it('calls the mocked converter', function () {
|
|
80
|
+
expect(converter.revert).toHaveBeenCalledOnce();
|
|
81
|
+
expect(converter.revert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
|
|
82
|
+
});
|
|
83
|
+
it('calls the mocked adapter', function () {
|
|
84
|
+
expect(booleanAdapter.revert).toHaveBeenCalledOnce();
|
|
85
|
+
expect(booleanAdapter.revert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { expectDefined, expectEquals, } from '@strictly/base';
|
|
2
|
+
import { identityAdapter, } from 'core/mobx/field_adapter_builder';
|
|
3
|
+
import { mergeAdaptersWithValidators, } from 'core/mobx/merge_field_adapters_with_validators';
|
|
4
|
+
import { UnreliableFieldConversionType } from 'types/field_converters';
|
|
5
|
+
import { createMockedAdapter, resetMockAdapter, } from './fixtures';
|
|
6
|
+
const error1 = 'error 1';
|
|
7
|
+
const error2 = 'error 2';
|
|
8
|
+
const context = 'context 1';
|
|
9
|
+
describe('MergedOfFieldAdaptersWithValidators', function () {
|
|
10
|
+
describe('empty validators', function () {
|
|
11
|
+
it('does not change the adapters', function () {
|
|
12
|
+
expectTypeOf().toEqualTypeOf();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe('different errors', function () {
|
|
16
|
+
it('merges the error types', function () {
|
|
17
|
+
expectTypeOf().toEqualTypeOf();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe('different paths', function () {
|
|
21
|
+
it('merges the error types', function () {
|
|
22
|
+
expectTypeOf().toEqualTypeOf();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('different values', function () {
|
|
26
|
+
it('removes mismatched values', function () {
|
|
27
|
+
expectTypeOf().toEqualTypeOf();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
const originalIntegerToIntegerAdapter = identityAdapter(0);
|
|
32
|
+
const originalBooleanToBooleanAdapter = identityAdapter(false, true);
|
|
33
|
+
describe('mergeFieldAdaptersWithValidators', function () {
|
|
34
|
+
const integerToIntegerAdapter = createMockedAdapter(originalIntegerToIntegerAdapter);
|
|
35
|
+
const booleanToBooleanAdapter = createMockedAdapter(originalBooleanToBooleanAdapter);
|
|
36
|
+
const failingValidator1 = vi.fn(function () {
|
|
37
|
+
return 'fail 1';
|
|
38
|
+
});
|
|
39
|
+
const failingValidator2 = vi.fn(function () {
|
|
40
|
+
return 'fail 2';
|
|
41
|
+
});
|
|
42
|
+
const requiredValidator = {
|
|
43
|
+
validate: () => null,
|
|
44
|
+
annotations: () => ({
|
|
45
|
+
required: true,
|
|
46
|
+
readonly: false,
|
|
47
|
+
}),
|
|
48
|
+
};
|
|
49
|
+
const readonlyValidator = {
|
|
50
|
+
validate: () => null,
|
|
51
|
+
annotations: () => ({
|
|
52
|
+
required: false,
|
|
53
|
+
readonly: true,
|
|
54
|
+
}),
|
|
55
|
+
};
|
|
56
|
+
beforeEach(function () {
|
|
57
|
+
resetMockAdapter(originalIntegerToIntegerAdapter, integerToIntegerAdapter);
|
|
58
|
+
resetMockAdapter(originalBooleanToBooleanAdapter, booleanToBooleanAdapter);
|
|
59
|
+
failingValidator1.mockClear();
|
|
60
|
+
failingValidator2.mockClear();
|
|
61
|
+
});
|
|
62
|
+
describe('record contents', function () {
|
|
63
|
+
describe('empty validators', function () {
|
|
64
|
+
const adapters = {
|
|
65
|
+
a: integerToIntegerAdapter,
|
|
66
|
+
b: booleanToBooleanAdapter,
|
|
67
|
+
};
|
|
68
|
+
const validators = {};
|
|
69
|
+
const merged = mergeAdaptersWithValidators(adapters, validators);
|
|
70
|
+
it('does not change the adapters', function () {
|
|
71
|
+
expect(merged).toEqual(adapters);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('populated validators', function () {
|
|
75
|
+
const adapters = {
|
|
76
|
+
a: integerToIntegerAdapter,
|
|
77
|
+
b: booleanToBooleanAdapter,
|
|
78
|
+
c: integerToIntegerAdapter,
|
|
79
|
+
d: integerToIntegerAdapter,
|
|
80
|
+
};
|
|
81
|
+
const validators = {
|
|
82
|
+
a: failingValidator1,
|
|
83
|
+
b: failingValidator2,
|
|
84
|
+
c: requiredValidator,
|
|
85
|
+
d: readonlyValidator,
|
|
86
|
+
};
|
|
87
|
+
const merged = mergeAdaptersWithValidators(adapters, validators);
|
|
88
|
+
describe('matching validators', function () {
|
|
89
|
+
it('has the same keys', function () {
|
|
90
|
+
expect([...Object.keys(adapters)]).toEqual([
|
|
91
|
+
'a',
|
|
92
|
+
'b',
|
|
93
|
+
'c',
|
|
94
|
+
'd',
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('revert', function () {
|
|
99
|
+
it.each([
|
|
100
|
+
[
|
|
101
|
+
'a',
|
|
102
|
+
'fail 1',
|
|
103
|
+
1,
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
'b',
|
|
107
|
+
'fail 2',
|
|
108
|
+
true,
|
|
109
|
+
],
|
|
110
|
+
])('field %s fails with validation %s', function (key, error, value) {
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
112
|
+
const mergedAdapter = merged[key];
|
|
113
|
+
expectDefined(mergedAdapter.revert);
|
|
114
|
+
const result = mergedAdapter.revert(value, key, null);
|
|
115
|
+
expectEquals(result.type, UnreliableFieldConversionType.Failure);
|
|
116
|
+
expect(result.error).toEqual(error);
|
|
117
|
+
});
|
|
118
|
+
it.each([
|
|
119
|
+
[
|
|
120
|
+
'c',
|
|
121
|
+
1,
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
'd',
|
|
125
|
+
true,
|
|
126
|
+
],
|
|
127
|
+
])('field %s succeeds with value %s', function (key, value) {
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
129
|
+
const mergedAdapter = merged[key];
|
|
130
|
+
expectDefined(mergedAdapter.revert);
|
|
131
|
+
const result = mergedAdapter.revert(value, key, null);
|
|
132
|
+
expectEquals(result.type, UnreliableFieldConversionType.Success);
|
|
133
|
+
expect(result.value).toEqual(value);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('convert', function () {
|
|
137
|
+
it.each([
|
|
138
|
+
[
|
|
139
|
+
'a',
|
|
140
|
+
false,
|
|
141
|
+
false,
|
|
142
|
+
1,
|
|
143
|
+
],
|
|
144
|
+
[
|
|
145
|
+
'b',
|
|
146
|
+
true,
|
|
147
|
+
false,
|
|
148
|
+
true,
|
|
149
|
+
],
|
|
150
|
+
[
|
|
151
|
+
'c',
|
|
152
|
+
true,
|
|
153
|
+
false,
|
|
154
|
+
2,
|
|
155
|
+
],
|
|
156
|
+
[
|
|
157
|
+
'd',
|
|
158
|
+
false,
|
|
159
|
+
true,
|
|
160
|
+
3,
|
|
161
|
+
],
|
|
162
|
+
])('field %s is required %s and readonly %s', function (key, expectedRequired, expectedReadonly, value) {
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
164
|
+
const adapter = merged[key];
|
|
165
|
+
const { required, readonly, } = adapter.convert(value, key, null);
|
|
166
|
+
expect(required).toEqual(expectedRequired);
|
|
167
|
+
expect(readonly).toEqual(expectedReadonly);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|