ux4g-components-web 1.4.0 → 1.5.0
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/README.md +76 -0
- package/dist/__tests__/css-bundle.integration.test.d.ts +11 -0
- package/dist/__tests__/css-bundle.integration.test.js +1102 -0
- package/dist/__tests__/css-bundle.phase10.property.test.d.ts +9 -0
- package/dist/__tests__/css-bundle.phase10.property.test.js +64 -0
- package/dist/__tests__/css-bundle.phase5.property.test.d.ts +9 -0
- package/dist/__tests__/css-bundle.phase5.property.test.js +126 -0
- package/dist/__tests__/css-bundle.phase6.property.test.d.ts +9 -0
- package/dist/__tests__/css-bundle.phase6.property.test.js +73 -0
- package/dist/__tests__/css-bundle.phase7.property.test.d.ts +9 -0
- package/dist/__tests__/css-bundle.phase7.property.test.js +76 -0
- package/dist/__tests__/css-bundle.phase8.property.test.d.ts +9 -0
- package/dist/__tests__/css-bundle.phase8.property.test.js +67 -0
- package/dist/__tests__/css-bundle.phase9.property.test.d.ts +9 -0
- package/dist/__tests__/css-bundle.phase9.property.test.js +93 -0
- package/dist/__tests__/css-bundle.property.test.d.ts +14 -0
- package/dist/__tests__/css-bundle.property.test.js +393 -0
- package/dist/__tests__/dom-generators.determinism.property.test.d.ts +1 -0
- package/dist/__tests__/dom-generators.determinism.property.test.js +71 -0
- package/dist/__tests__/dom-generators.id.property.test.d.ts +1 -0
- package/dist/__tests__/dom-generators.id.property.test.js +99 -0
- package/dist/__tests__/dom-generators.otp.property.test.d.ts +1 -0
- package/dist/__tests__/dom-generators.property.test.d.ts +1 -0
- package/dist/__tests__/dom-generators.property.test.js +205 -0
- package/dist/__tests__/dom-generators.states.property.test.d.ts +1 -0
- package/dist/__tests__/dom-generators.table.property.test.d.ts +1 -0
- package/dist/__tests__/dom-generators.tier1.property.test.d.ts +1 -0
- package/dist/__tests__/dom-generators.tier1.property.test.js +403 -0
- package/dist/__tests__/dom-generators.validation.property.test.d.ts +1 -0
- package/dist/__tests__/dom-generators.validation.property.test.js +327 -0
- package/dist/__tests__/megamenu.classbuilder.property.test.d.ts +1 -0
- package/dist/__tests__/megamenu.classbuilder.property.test.js +88 -0
- package/dist/__tests__/smoke.test.d.ts +1 -0
- package/dist/__tests__/smoke.test.js +65 -0
- package/dist/__tests__/types.phase10.property.test.d.ts +1 -0
- package/dist/__tests__/types.phase10.property.test.js +166 -0
- package/dist/__tests__/types.phase10.test.d.ts +1 -0
- package/dist/__tests__/types.phase10.test.js +76 -0
- package/dist/__tests__/types.phase3.property.test.d.ts +1 -0
- package/dist/__tests__/types.phase3.property.test.js +83 -0
- package/dist/__tests__/types.phase3.test.d.ts +1 -0
- package/dist/__tests__/types.phase3.test.js +76 -0
- package/dist/__tests__/types.phase4.property.test.d.ts +1 -0
- package/dist/__tests__/types.phase4.property.test.js +119 -0
- package/dist/__tests__/types.phase4.test.d.ts +1 -0
- package/dist/__tests__/types.phase4.test.js +70 -0
- package/dist/__tests__/types.phase5.property.test.d.ts +1 -0
- package/dist/__tests__/types.phase5.property.test.js +120 -0
- package/dist/__tests__/types.phase5.test.d.ts +1 -0
- package/dist/__tests__/types.phase5.test.js +64 -0
- package/dist/__tests__/types.phase6.property.test.d.ts +1 -0
- package/dist/__tests__/types.phase6.property.test.js +189 -0
- package/dist/__tests__/types.phase6.test.d.ts +1 -0
- package/dist/__tests__/types.phase6.test.js +121 -0
- package/dist/__tests__/types.phase7.property.test.d.ts +1 -0
- package/dist/__tests__/types.phase7.property.test.js +217 -0
- package/dist/__tests__/types.phase7.test.d.ts +1 -0
- package/dist/__tests__/types.phase7.test.js +106 -0
- package/dist/__tests__/types.phase8.property.test.d.ts +1 -0
- package/dist/__tests__/types.phase8.property.test.js +224 -0
- package/dist/__tests__/types.phase8.test.d.ts +1 -0
- package/dist/__tests__/types.phase8.test.js +114 -0
- package/dist/__tests__/types.phase9.property.test.d.ts +1 -0
- package/dist/__tests__/types.phase9.property.test.js +347 -0
- package/dist/__tests__/types.phase9.test.d.ts +1 -0
- package/dist/__tests__/types.phase9.test.js +226 -0
- package/dist/__tests__/types.restructure.property.test.d.ts +1 -0
- package/dist/__tests__/types.restructure.property.test.js +76 -0
- package/dist/__tests__/types.test.d.ts +1 -0
- package/dist/__tests__/types.test.js +175 -0
- package/dist/dom-generators/accordion.d.ts +23 -0
- package/dist/dom-generators/avatar.d.ts +19 -0
- package/dist/dom-generators/carousel.d.ts +20 -0
- package/dist/dom-generators/chip.d.ts +18 -0
- package/dist/dom-generators/combobox.d.ts +28 -0
- package/dist/dom-generators/date-picker.d.ts +19 -0
- package/dist/dom-generators/dom-generators/accordion.d.ts +21 -0
- package/dist/dom-generators/dom-generators/avatar.d.ts +17 -0
- package/dist/dom-generators/dom-generators/carousel.d.ts +19 -0
- package/dist/dom-generators/dom-generators/chip.d.ts +16 -0
- package/dist/dom-generators/dom-generators/combobox.d.ts +26 -0
- package/dist/dom-generators/dom-generators/date-picker.d.ts +18 -0
- package/dist/dom-generators/dom-generators/drawer.d.ts +17 -0
- package/dist/dom-generators/dom-generators/dropdown.d.ts +26 -0
- package/dist/dom-generators/dom-generators/file-upload.d.ts +20 -0
- package/dist/dom-generators/dom-generators/id-generator.d.ts +9 -0
- package/dist/dom-generators/dom-generators/index.d.ts +27 -0
- package/dist/dom-generators/dom-generators/modal.d.ts +19 -0
- package/dist/dom-generators/dom-generators/otp.d.ts +16 -0
- package/dist/dom-generators/dom-generators/popover.d.ts +17 -0
- package/dist/dom-generators/dom-generators/progress.d.ts +16 -0
- package/dist/dom-generators/dom-generators/search.d.ts +20 -0
- package/dist/dom-generators/dom-generators/stepper.d.ts +21 -0
- package/dist/dom-generators/dom-generators/table.d.ts +23 -0
- package/dist/dom-generators/dom-generators/tabs.d.ts +21 -0
- package/dist/dom-generators/dom-generators/time-picker.d.ts +18 -0
- package/dist/dom-generators/dom-generators/tooltip.d.ts +17 -0
- package/dist/dom-generators/dom-generators/types.d.ts +27 -0
- package/dist/dom-generators/dom-generators/validate.d.ts +20 -0
- package/dist/dom-generators/drawer.d.ts +19 -0
- package/dist/dom-generators/dropdown.d.ts +28 -0
- package/dist/dom-generators/file-upload.d.ts +22 -0
- package/dist/dom-generators/id-generator.d.ts +9 -0
- package/dist/dom-generators/index.bundled.d.ts +654 -0
- package/dist/dom-generators/index.cjs +2029 -0
- package/dist/dom-generators/index.d.ts +27 -0
- package/dist/dom-generators/index.mjs +2001 -0
- package/dist/dom-generators/modal.d.ts +21 -0
- package/dist/dom-generators/otp.d.ts +18 -0
- package/dist/dom-generators/popover.d.ts +19 -0
- package/dist/dom-generators/progress.d.ts +18 -0
- package/dist/dom-generators/search.d.ts +22 -0
- package/dist/dom-generators/stepper.d.ts +23 -0
- package/dist/dom-generators/table.d.ts +25 -0
- package/dist/dom-generators/tabs.d.ts +23 -0
- package/dist/dom-generators/time-picker.d.ts +19 -0
- package/dist/dom-generators/tooltip.d.ts +19 -0
- package/dist/dom-generators/types.d.ts +155 -0
- package/dist/dom-generators/validate.d.ts +20 -0
- package/dist/runtime/bootstrap.js +59 -0
- package/dist/runtime/index.js +55 -0
- package/dist/types.d.ts +155 -0
- package/dist/types.js +552 -0
- package/package.json +12 -2
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property-based tests for DOM Generator validation helpers
|
|
3
|
+
* Property 6: Invalid props throw descriptive errors
|
|
4
|
+
* Tag: Feature: ux4g-native-framework-experience, Property 6: Invalid props throw descriptive errors
|
|
5
|
+
*
|
|
6
|
+
* Validates: Requirements 3.7
|
|
7
|
+
*/
|
|
8
|
+
import * as fc from 'fast-check';
|
|
9
|
+
import { DOMGeneratorValidationError } from '../dom-generators/types';
|
|
10
|
+
import { validateRequired, validateType, validateEnum, validateRange, validateArray, } from '../dom-generators/validate';
|
|
11
|
+
/**
|
|
12
|
+
* Property 6.1: validateRequired throws on null/undefined
|
|
13
|
+
*
|
|
14
|
+
* For any component name and prop name, calling validateRequired with null or
|
|
15
|
+
* undefined throws DOMGeneratorValidationError.
|
|
16
|
+
*
|
|
17
|
+
* Tag: Feature: ux4g-native-framework-experience, Property 6: Invalid props throw descriptive errors
|
|
18
|
+
* Validates: Requirements 3.7
|
|
19
|
+
*/
|
|
20
|
+
describe('Property 6.1: validateRequired throws on null/undefined', () => {
|
|
21
|
+
it('throws DOMGeneratorValidationError for null values', () => {
|
|
22
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), (componentName, propName) => {
|
|
23
|
+
try {
|
|
24
|
+
validateRequired(componentName, propName, null);
|
|
25
|
+
return false; // Should have thrown
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
return e instanceof DOMGeneratorValidationError;
|
|
29
|
+
}
|
|
30
|
+
}), { numRuns: 100 });
|
|
31
|
+
});
|
|
32
|
+
it('throws DOMGeneratorValidationError for undefined values', () => {
|
|
33
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), (componentName, propName) => {
|
|
34
|
+
try {
|
|
35
|
+
validateRequired(componentName, propName, undefined);
|
|
36
|
+
return false; // Should have thrown
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
return e instanceof DOMGeneratorValidationError;
|
|
40
|
+
}
|
|
41
|
+
}), { numRuns: 100 });
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Property 6.2: validateRequired does NOT throw on valid values
|
|
46
|
+
*
|
|
47
|
+
* For any non-null/undefined value, validateRequired does not throw.
|
|
48
|
+
*
|
|
49
|
+
* Tag: Feature: ux4g-native-framework-experience, Property 6: Invalid props throw descriptive errors
|
|
50
|
+
* Validates: Requirements 3.7
|
|
51
|
+
*/
|
|
52
|
+
describe('Property 6.2: validateRequired does NOT throw on valid values', () => {
|
|
53
|
+
it('does not throw for non-null/undefined values', () => {
|
|
54
|
+
const nonNullArb = fc.oneof(fc.string(), fc.integer(), fc.boolean(), fc.constant(0), fc.constant(''), fc.constant(false), fc.constant([]), fc.constant({}));
|
|
55
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), nonNullArb, (componentName, propName, value) => {
|
|
56
|
+
try {
|
|
57
|
+
validateRequired(componentName, propName, value);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}), { numRuns: 100 });
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* Property 6.3: validateType throws on wrong type
|
|
68
|
+
*
|
|
69
|
+
* For any value where typeof value !== expectedType, validateType throws
|
|
70
|
+
* DOMGeneratorValidationError with the prop name and value in the message.
|
|
71
|
+
*
|
|
72
|
+
* Tag: Feature: ux4g-native-framework-experience, Property 6: Invalid props throw descriptive errors
|
|
73
|
+
* Validates: Requirements 3.7
|
|
74
|
+
*/
|
|
75
|
+
describe('Property 6.3: validateType throws on wrong type', () => {
|
|
76
|
+
it('throws DOMGeneratorValidationError when typeof value !== expectedType', () => {
|
|
77
|
+
// Generate value/expectedType pairs where typeof value !== expectedType
|
|
78
|
+
const mismatchArb = fc.oneof(
|
|
79
|
+
// string value but expecting number
|
|
80
|
+
fc.tuple(fc.string(), fc.constant('number')),
|
|
81
|
+
// number value but expecting string
|
|
82
|
+
fc.tuple(fc.integer(), fc.constant('string')),
|
|
83
|
+
// boolean value but expecting string
|
|
84
|
+
fc.tuple(fc.boolean(), fc.constant('string')),
|
|
85
|
+
// number value but expecting boolean
|
|
86
|
+
fc.tuple(fc.integer(), fc.constant('boolean')),
|
|
87
|
+
// string value but expecting boolean
|
|
88
|
+
fc.tuple(fc.string(), fc.constant('boolean')),
|
|
89
|
+
// boolean value but expecting number
|
|
90
|
+
fc.tuple(fc.boolean(), fc.constant('number')));
|
|
91
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), mismatchArb, (componentName, propName, [value, expectedType]) => {
|
|
92
|
+
try {
|
|
93
|
+
validateType(componentName, propName, value, expectedType);
|
|
94
|
+
return false; // Should have thrown
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
if (!(e instanceof DOMGeneratorValidationError))
|
|
98
|
+
return false;
|
|
99
|
+
// Error message must contain the prop name and value
|
|
100
|
+
if (!e.message.includes(propName))
|
|
101
|
+
return false;
|
|
102
|
+
if (!e.message.includes(JSON.stringify(value)))
|
|
103
|
+
return false;
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
}), { numRuns: 100 });
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
/**
|
|
110
|
+
* Property 6.4: validateEnum throws on non-member values
|
|
111
|
+
*
|
|
112
|
+
* For any value not in the allowed set, validateEnum throws with the value
|
|
113
|
+
* in the error message.
|
|
114
|
+
*
|
|
115
|
+
* Tag: Feature: ux4g-native-framework-experience, Property 6: Invalid props throw descriptive errors
|
|
116
|
+
* Validates: Requirements 3.7
|
|
117
|
+
*/
|
|
118
|
+
describe('Property 6.4: validateEnum throws on non-member values', () => {
|
|
119
|
+
it('throws DOMGeneratorValidationError for values not in allowedValues', () => {
|
|
120
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), fc.array(fc.string({ minLength: 1 }), { minLength: 1, maxLength: 5 }), (componentName, propName, value, allowedValues) => {
|
|
121
|
+
// Ensure value is NOT in allowedValues
|
|
122
|
+
if (allowedValues.includes(value))
|
|
123
|
+
return true; // skip this case
|
|
124
|
+
try {
|
|
125
|
+
validateEnum(componentName, propName, value, allowedValues);
|
|
126
|
+
return false; // Should have thrown
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
if (!(e instanceof DOMGeneratorValidationError))
|
|
130
|
+
return false;
|
|
131
|
+
// Error message must contain the value
|
|
132
|
+
if (!e.message.includes(JSON.stringify(value)))
|
|
133
|
+
return false;
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
}), { numRuns: 100 });
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* Property 6.5: validateRange throws on out-of-range numbers
|
|
141
|
+
*
|
|
142
|
+
* For numbers outside [min, max], validateRange throws.
|
|
143
|
+
*
|
|
144
|
+
* Tag: Feature: ux4g-native-framework-experience, Property 6: Invalid props throw descriptive errors
|
|
145
|
+
* Validates: Requirements 3.7
|
|
146
|
+
*/
|
|
147
|
+
describe('Property 6.5: validateRange throws on out-of-range numbers', () => {
|
|
148
|
+
it('throws DOMGeneratorValidationError for numbers below min', () => {
|
|
149
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), fc.integer({ min: 0, max: 100 }), fc.integer({ min: 101, max: 1000 }), (componentName, propName, min, max) => {
|
|
150
|
+
const value = min - 1; // Below min
|
|
151
|
+
try {
|
|
152
|
+
validateRange(componentName, propName, value, min, max);
|
|
153
|
+
return false; // Should have thrown
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
return e instanceof DOMGeneratorValidationError;
|
|
157
|
+
}
|
|
158
|
+
}), { numRuns: 100 });
|
|
159
|
+
});
|
|
160
|
+
it('throws DOMGeneratorValidationError for numbers above max', () => {
|
|
161
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), fc.integer({ min: 0, max: 100 }), fc.integer({ min: 101, max: 1000 }), (componentName, propName, min, max) => {
|
|
162
|
+
const value = max + 1; // Above max
|
|
163
|
+
try {
|
|
164
|
+
validateRange(componentName, propName, value, min, max);
|
|
165
|
+
return false; // Should have thrown
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
return e instanceof DOMGeneratorValidationError;
|
|
169
|
+
}
|
|
170
|
+
}), { numRuns: 100 });
|
|
171
|
+
});
|
|
172
|
+
it('throws DOMGeneratorValidationError for non-number values', () => {
|
|
173
|
+
const nonNumberArb = fc.oneof(fc.string(), fc.boolean(), fc.constant(null), fc.constant(undefined));
|
|
174
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), nonNumberArb, (componentName, propName, value) => {
|
|
175
|
+
try {
|
|
176
|
+
validateRange(componentName, propName, value, 0, 100);
|
|
177
|
+
return false; // Should have thrown
|
|
178
|
+
}
|
|
179
|
+
catch (e) {
|
|
180
|
+
return e instanceof DOMGeneratorValidationError;
|
|
181
|
+
}
|
|
182
|
+
}), { numRuns: 100 });
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
/**
|
|
186
|
+
* Property 6.6: validateArray throws on non-arrays
|
|
187
|
+
*
|
|
188
|
+
* For any non-array value, validateArray throws.
|
|
189
|
+
*
|
|
190
|
+
* Tag: Feature: ux4g-native-framework-experience, Property 6: Invalid props throw descriptive errors
|
|
191
|
+
* Validates: Requirements 3.7
|
|
192
|
+
*/
|
|
193
|
+
describe('Property 6.6: validateArray throws on non-arrays', () => {
|
|
194
|
+
it('throws DOMGeneratorValidationError for non-array values', () => {
|
|
195
|
+
const nonArrayArb = fc.oneof(fc.string(), fc.integer(), fc.boolean(), fc.constant(null), fc.constant(undefined), fc.constant({}), fc.constant(42));
|
|
196
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), nonArrayArb, (componentName, propName, value) => {
|
|
197
|
+
try {
|
|
198
|
+
validateArray(componentName, propName, value);
|
|
199
|
+
return false; // Should have thrown
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
return e instanceof DOMGeneratorValidationError;
|
|
203
|
+
}
|
|
204
|
+
}), { numRuns: 100 });
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
/**
|
|
208
|
+
* Property 6.7: Error messages contain prop name and value
|
|
209
|
+
*
|
|
210
|
+
* All thrown errors have propName and propValue properties matching what was passed in.
|
|
211
|
+
*
|
|
212
|
+
* Tag: Feature: ux4g-native-framework-experience, Property 6: Invalid props throw descriptive errors
|
|
213
|
+
* Validates: Requirements 3.7
|
|
214
|
+
*/
|
|
215
|
+
describe('Property 6.7: Error messages contain prop name and value', () => {
|
|
216
|
+
it('thrown errors have propName and propValue properties matching input', () => {
|
|
217
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), (componentName, propName) => {
|
|
218
|
+
try {
|
|
219
|
+
validateRequired(componentName, propName, null);
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
if (!(e instanceof DOMGeneratorValidationError))
|
|
224
|
+
return false;
|
|
225
|
+
if (e.propName !== propName)
|
|
226
|
+
return false;
|
|
227
|
+
if (e.propValue !== null)
|
|
228
|
+
return false;
|
|
229
|
+
if (e.componentName !== componentName)
|
|
230
|
+
return false;
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
}), { numRuns: 100 });
|
|
234
|
+
});
|
|
235
|
+
it('validateType errors have correct propName and propValue', () => {
|
|
236
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), fc.string(), (componentName, propName, value) => {
|
|
237
|
+
try {
|
|
238
|
+
validateType(componentName, propName, value, 'number');
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
catch (e) {
|
|
242
|
+
if (!(e instanceof DOMGeneratorValidationError))
|
|
243
|
+
return false;
|
|
244
|
+
if (e.propName !== propName)
|
|
245
|
+
return false;
|
|
246
|
+
if (e.propValue !== value)
|
|
247
|
+
return false;
|
|
248
|
+
if (e.componentName !== componentName)
|
|
249
|
+
return false;
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
}), { numRuns: 100 });
|
|
253
|
+
});
|
|
254
|
+
it('validateEnum errors have correct propName and propValue', () => {
|
|
255
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), fc.integer(), (componentName, propName, value) => {
|
|
256
|
+
try {
|
|
257
|
+
validateEnum(componentName, propName, value, ['a', 'b', 'c']);
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
if (!(e instanceof DOMGeneratorValidationError))
|
|
262
|
+
return false;
|
|
263
|
+
if (e.propName !== propName)
|
|
264
|
+
return false;
|
|
265
|
+
if (e.propValue !== value)
|
|
266
|
+
return false;
|
|
267
|
+
if (e.componentName !== componentName)
|
|
268
|
+
return false;
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
}), { numRuns: 100 });
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
/**
|
|
275
|
+
* Property 6.8: Error name is DOMGeneratorValidationError
|
|
276
|
+
*
|
|
277
|
+
* All thrown errors have name === 'DOMGeneratorValidationError'.
|
|
278
|
+
*
|
|
279
|
+
* Tag: Feature: ux4g-native-framework-experience, Property 6: Invalid props throw descriptive errors
|
|
280
|
+
* Validates: Requirements 3.7
|
|
281
|
+
*/
|
|
282
|
+
describe('Property 6.8: Error name is DOMGeneratorValidationError', () => {
|
|
283
|
+
it('all validation errors have name === "DOMGeneratorValidationError"', () => {
|
|
284
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), fc.string({ minLength: 1 }), (componentName, propName) => {
|
|
285
|
+
const errors = [];
|
|
286
|
+
// validateRequired
|
|
287
|
+
try {
|
|
288
|
+
validateRequired(componentName, propName, null);
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
errors.push(e);
|
|
292
|
+
}
|
|
293
|
+
// validateType
|
|
294
|
+
try {
|
|
295
|
+
validateType(componentName, propName, 'string', 'number');
|
|
296
|
+
}
|
|
297
|
+
catch (e) {
|
|
298
|
+
errors.push(e);
|
|
299
|
+
}
|
|
300
|
+
// validateEnum
|
|
301
|
+
try {
|
|
302
|
+
validateEnum(componentName, propName, 'invalid', ['a', 'b']);
|
|
303
|
+
}
|
|
304
|
+
catch (e) {
|
|
305
|
+
errors.push(e);
|
|
306
|
+
}
|
|
307
|
+
// validateRange
|
|
308
|
+
try {
|
|
309
|
+
validateRange(componentName, propName, -1, 0, 100);
|
|
310
|
+
}
|
|
311
|
+
catch (e) {
|
|
312
|
+
errors.push(e);
|
|
313
|
+
}
|
|
314
|
+
// validateArray
|
|
315
|
+
try {
|
|
316
|
+
validateArray(componentName, propName, 'not-an-array');
|
|
317
|
+
}
|
|
318
|
+
catch (e) {
|
|
319
|
+
errors.push(e);
|
|
320
|
+
}
|
|
321
|
+
// All errors must have the correct name
|
|
322
|
+
if (errors.length !== 5)
|
|
323
|
+
return false;
|
|
324
|
+
return errors.every((e) => e.name === 'DOMGeneratorValidationError');
|
|
325
|
+
}), { numRuns: 100 });
|
|
326
|
+
});
|
|
327
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/__tests__/megamenu.classbuilder.property.test.ts
|
|
2
|
+
// Property-based tests for Mega Menu class builder functions
|
|
3
|
+
// Feature: ux4g-megamenu-component
|
|
4
|
+
import * as fc from 'fast-check';
|
|
5
|
+
import { buildMegaMenuClasses, buildMegaMenuWrapperClasses, } from '../../src/types';
|
|
6
|
+
// Helper: split result into tokens
|
|
7
|
+
const tokens = (s) => s.split(' ');
|
|
8
|
+
// Arbitrary for extra class string: lowercase alphanumeric with hyphens, non-empty, no spaces
|
|
9
|
+
const extraArb = fc.stringMatching(/^[a-z][a-z0-9-]*$/).filter(s => s.length > 0 && s.length <= 30);
|
|
10
|
+
// ── Property 1: buildMegaMenuClasses produces correct class string for all input combinations ──
|
|
11
|
+
// **Validates: Requirements 1.2, 1.3, 1.4, 1.5, 1.6**
|
|
12
|
+
describe('Feature: ux4g-megamenu-component, Property 1: buildMegaMenuClasses produces correct class string for all input combinations', () => {
|
|
13
|
+
it('always starts with ux4g-mega-menu, contains --dropdown iff dropdown=true, contains --dropdown-right iff both dropdown AND dropdownRight are true, ends with extra if non-empty', () => {
|
|
14
|
+
fc.assert(fc.property(fc.boolean(), fc.boolean(), fc.oneof(fc.constant(undefined), extraArb), (dropdown, dropdownRight, extra) => {
|
|
15
|
+
const result = buildMegaMenuClasses(dropdown, dropdownRight, extra);
|
|
16
|
+
const toks = tokens(result);
|
|
17
|
+
// Always starts with 'ux4g-mega-menu' as the first class
|
|
18
|
+
expect(toks[0]).toBe('ux4g-mega-menu');
|
|
19
|
+
// Contains 'ux4g-mega-menu--dropdown' if and only if dropdown is true
|
|
20
|
+
if (dropdown) {
|
|
21
|
+
expect(toks).toContain('ux4g-mega-menu--dropdown');
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
expect(toks).not.toContain('ux4g-mega-menu--dropdown');
|
|
25
|
+
}
|
|
26
|
+
// Contains 'ux4g-mega-menu--dropdown-right' if and only if both dropdown AND dropdownRight are true
|
|
27
|
+
if (dropdown && dropdownRight) {
|
|
28
|
+
expect(toks).toContain('ux4g-mega-menu--dropdown-right');
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
expect(toks).not.toContain('ux4g-mega-menu--dropdown-right');
|
|
32
|
+
}
|
|
33
|
+
// Ends with extra string if provided and non-empty
|
|
34
|
+
if (extra !== undefined) {
|
|
35
|
+
expect(toks[toks.length - 1]).toBe(extra);
|
|
36
|
+
}
|
|
37
|
+
// No duplicate tokens
|
|
38
|
+
expect(new Set(toks).size).toBe(toks.length);
|
|
39
|
+
}), { numRuns: 100 });
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
// ── Property 2: buildMegaMenuWrapperClasses produces correct class string for all input combinations ──
|
|
43
|
+
// **Validates: Requirements 1.7, 1.8, 1.9**
|
|
44
|
+
describe('Feature: ux4g-megamenu-component, Property 2: buildMegaMenuWrapperClasses produces correct class string for all input combinations', () => {
|
|
45
|
+
it('always starts with ux4g-mega-menu-wrapper, contains --active iff active=true, ends with extra if non-empty', () => {
|
|
46
|
+
fc.assert(fc.property(fc.boolean(), fc.oneof(fc.constant(undefined), extraArb), (active, extra) => {
|
|
47
|
+
const result = buildMegaMenuWrapperClasses(active, extra);
|
|
48
|
+
const toks = tokens(result);
|
|
49
|
+
// Always starts with 'ux4g-mega-menu-wrapper' as the first class
|
|
50
|
+
expect(toks[0]).toBe('ux4g-mega-menu-wrapper');
|
|
51
|
+
// Contains 'ux4g-mega-menu-wrapper--active' if and only if active is true
|
|
52
|
+
if (active) {
|
|
53
|
+
expect(toks).toContain('ux4g-mega-menu-wrapper--active');
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
expect(toks).not.toContain('ux4g-mega-menu-wrapper--active');
|
|
57
|
+
}
|
|
58
|
+
// Ends with extra string if provided and non-empty
|
|
59
|
+
if (extra !== undefined) {
|
|
60
|
+
expect(toks[toks.length - 1]).toBe(extra);
|
|
61
|
+
}
|
|
62
|
+
// No duplicate tokens
|
|
63
|
+
expect(new Set(toks).size).toBe(toks.length);
|
|
64
|
+
}), { numRuns: 100 });
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
// ── Property 3: Class builder functions are pure (deterministic) ──────────────
|
|
68
|
+
// **Validates: Requirements 8.4**
|
|
69
|
+
describe('Feature: ux4g-megamenu-component, Property 3: Class builder functions are pure (deterministic)', () => {
|
|
70
|
+
it('buildMegaMenuClasses returns identical output for identical inputs across multiple calls', () => {
|
|
71
|
+
fc.assert(fc.property(fc.boolean(), fc.boolean(), fc.oneof(fc.constant(undefined), extraArb), (dropdown, dropdownRight, extra) => {
|
|
72
|
+
const result1 = buildMegaMenuClasses(dropdown, dropdownRight, extra);
|
|
73
|
+
const result2 = buildMegaMenuClasses(dropdown, dropdownRight, extra);
|
|
74
|
+
const result3 = buildMegaMenuClasses(dropdown, dropdownRight, extra);
|
|
75
|
+
expect(result1).toBe(result2);
|
|
76
|
+
expect(result2).toBe(result3);
|
|
77
|
+
}), { numRuns: 100 });
|
|
78
|
+
});
|
|
79
|
+
it('buildMegaMenuWrapperClasses returns identical output for identical inputs across multiple calls', () => {
|
|
80
|
+
fc.assert(fc.property(fc.boolean(), fc.oneof(fc.constant(undefined), extraArb), (active, extra) => {
|
|
81
|
+
const result1 = buildMegaMenuWrapperClasses(active, extra);
|
|
82
|
+
const result2 = buildMegaMenuWrapperClasses(active, extra);
|
|
83
|
+
const result3 = buildMegaMenuWrapperClasses(active, extra);
|
|
84
|
+
expect(result1).toBe(result2);
|
|
85
|
+
expect(result2).toBe(result3);
|
|
86
|
+
}), { numRuns: 100 });
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
const PKG_ROOT = path.resolve(__dirname, '../..');
|
|
4
|
+
describe('ux4g-components-web smoke tests', () => {
|
|
5
|
+
let pkg;
|
|
6
|
+
beforeAll(() => {
|
|
7
|
+
const pkgPath = path.join(PKG_ROOT, 'package.json');
|
|
8
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
9
|
+
});
|
|
10
|
+
// --- package.json identity ---
|
|
11
|
+
it('package name is "ux4g-components-web"', () => {
|
|
12
|
+
expect(pkg.name).toBe('ux4g-components-web');
|
|
13
|
+
});
|
|
14
|
+
// --- exports shape ---
|
|
15
|
+
it('exports["./styles.css"] exists and resolves to ./styles/ux4g.css', () => {
|
|
16
|
+
const exports = pkg.exports;
|
|
17
|
+
expect(exports).toBeDefined();
|
|
18
|
+
const stylesCss = exports['./styles.css'];
|
|
19
|
+
expect(stylesCss).toBeDefined();
|
|
20
|
+
expect(stylesCss['default']).toBe('./styles/ux4g.css');
|
|
21
|
+
});
|
|
22
|
+
it('exports["./types"] exists with types, import, require, default fields', () => {
|
|
23
|
+
const exports = pkg.exports;
|
|
24
|
+
const typesEntry = exports['./types'];
|
|
25
|
+
expect(typesEntry).toBeDefined();
|
|
26
|
+
expect(typesEntry['types']).toBeDefined();
|
|
27
|
+
expect(typesEntry['import']).toBeDefined();
|
|
28
|
+
expect(typesEntry['require']).toBeDefined();
|
|
29
|
+
expect(typesEntry['default']).toBeDefined();
|
|
30
|
+
});
|
|
31
|
+
// --- dist artifact existence ---
|
|
32
|
+
it('styles/ux4g.css exists and has non-zero size', () => {
|
|
33
|
+
const cssPath = path.join(PKG_ROOT, 'styles', 'ux4g.css');
|
|
34
|
+
expect(fs.existsSync(cssPath)).toBe(true);
|
|
35
|
+
expect(fs.statSync(cssPath).size).toBeGreaterThan(0);
|
|
36
|
+
});
|
|
37
|
+
it('dist/types/index.mjs exists', () => {
|
|
38
|
+
expect(fs.existsSync(path.join(PKG_ROOT, 'dist', 'types', 'index.mjs'))).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it('dist/types/index.cjs exists', () => {
|
|
41
|
+
expect(fs.existsSync(path.join(PKG_ROOT, 'dist', 'types', 'index.cjs'))).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
it('dist/types/index.d.ts exists', () => {
|
|
44
|
+
expect(fs.existsSync(path.join(PKG_ROOT, 'dist', 'types', 'index.d.ts'))).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
// --- no cross-framework contamination ---
|
|
47
|
+
it('has no react, react-dom, or @angular/* in any dependency field', () => {
|
|
48
|
+
const depFields = [
|
|
49
|
+
'dependencies',
|
|
50
|
+
'devDependencies',
|
|
51
|
+
'peerDependencies',
|
|
52
|
+
'optionalDependencies',
|
|
53
|
+
];
|
|
54
|
+
const forbidden = ['react', 'react-dom'];
|
|
55
|
+
for (const field of depFields) {
|
|
56
|
+
const deps = (pkg[field] ?? {});
|
|
57
|
+
for (const forbidden_pkg of forbidden) {
|
|
58
|
+
expect(Object.keys(deps)).not.toContain(forbidden_pkg);
|
|
59
|
+
}
|
|
60
|
+
// No @angular/* keys
|
|
61
|
+
const angularKeys = Object.keys(deps).filter((k) => k.startsWith('@angular/'));
|
|
62
|
+
expect(angularKeys).toHaveLength(0);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// src/__tests__/types.phase10.property.test.ts
|
|
2
|
+
// Property-based tests for all Phase 10 build functions
|
|
3
|
+
// Feature: ux4g-phase10-components
|
|
4
|
+
import * as fc from 'fast-check';
|
|
5
|
+
import { buildSlotGridClasses, buildFooterClasses, buildResultListRowClasses, } from '../../src/types';
|
|
6
|
+
// Helper: split result into tokens
|
|
7
|
+
const tokens = (s) => s.split(' ');
|
|
8
|
+
const hasDuplicates = (arr) => new Set(arr).size !== arr.length;
|
|
9
|
+
// ── Property 1: SlotGrid ──────────────────────────────────────────────────────
|
|
10
|
+
// **Validates: Requirements 1.2, 1.3, 1.7, 1.11, 6.7**
|
|
11
|
+
describe('Property 1: SlotGrid class is exactly the correct container class for each variant', () => {
|
|
12
|
+
it('variant determines container class exactly, mutually exclusive, no duplicates, extra appended, idempotent', () => {
|
|
13
|
+
fc.assert(fc.property(fc.constantFrom('weekly', 'compact'), fc.oneof(fc.constant(undefined), fc.stringMatching(/^[a-z][a-z0-9-]*$/).filter(s => s.length > 0 && s.length <= 20)), (variant, extra) => {
|
|
14
|
+
const result = buildSlotGridClasses(variant, extra);
|
|
15
|
+
const toks = tokens(result);
|
|
16
|
+
// variant='weekly' → contains weekly container, NOT compact
|
|
17
|
+
if (variant === 'weekly') {
|
|
18
|
+
expect(toks).toContain('ux4g-time-slot-weekly-container');
|
|
19
|
+
expect(toks).not.toContain('ux4g-time-slot-compact-container');
|
|
20
|
+
}
|
|
21
|
+
// variant='compact' → contains compact container, NOT weekly
|
|
22
|
+
if (variant === 'compact') {
|
|
23
|
+
expect(toks).toContain('ux4g-time-slot-compact-container');
|
|
24
|
+
expect(toks).not.toContain('ux4g-time-slot-weekly-container');
|
|
25
|
+
}
|
|
26
|
+
// result never contains both simultaneously
|
|
27
|
+
const hasWeekly = toks.includes('ux4g-time-slot-weekly-container');
|
|
28
|
+
const hasCompact = toks.includes('ux4g-time-slot-compact-container');
|
|
29
|
+
expect(hasWeekly && hasCompact).toBe(false);
|
|
30
|
+
// when extra provided, result contains extra token
|
|
31
|
+
if (extra !== undefined) {
|
|
32
|
+
expect(toks).toContain(extra);
|
|
33
|
+
}
|
|
34
|
+
// token array has no duplicates
|
|
35
|
+
expect(hasDuplicates(toks)).toBe(false);
|
|
36
|
+
// idempotent: calling twice with same inputs returns same string
|
|
37
|
+
const result2 = buildSlotGridClasses(variant, extra);
|
|
38
|
+
expect(result2).toBe(result);
|
|
39
|
+
}), { numRuns: 10 });
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
// ── Property 2: Footer ────────────────────────────────────────────────────────
|
|
43
|
+
// **Validates: Requirements 2.2, 2.3, 2.4, 2.9, 2.13, 6.8**
|
|
44
|
+
describe('Property 2: Footer class is exactly the correct theme class for each theme value', () => {
|
|
45
|
+
it('theme determines footer class exactly, empty for default, mutually exclusive, no duplicates, extra appended, idempotent', () => {
|
|
46
|
+
fc.assert(fc.property(fc.constantFrom('default', 'primary', 'dark'), fc.oneof(fc.constant(undefined), fc.stringMatching(/^[a-z][a-z0-9-]*$/).filter(s => s.length > 0 && s.length <= 20)), (theme, extra) => {
|
|
47
|
+
const result = buildFooterClasses(theme, extra);
|
|
48
|
+
// filter out empty string tokens before checking duplicates
|
|
49
|
+
const toks = result === '' ? [] : result.split(' ');
|
|
50
|
+
const nonEmptyToks = toks.filter(t => t !== '');
|
|
51
|
+
// theme='primary' → result contains 'ux4g-footer-primary'
|
|
52
|
+
if (theme === 'primary') {
|
|
53
|
+
expect(result).toContain('ux4g-footer-primary');
|
|
54
|
+
}
|
|
55
|
+
// theme='dark' → result contains 'ux4g-footer-dark'
|
|
56
|
+
if (theme === 'dark') {
|
|
57
|
+
expect(result).toContain('ux4g-footer-dark');
|
|
58
|
+
}
|
|
59
|
+
// theme='default' with no extra → result is exactly ''
|
|
60
|
+
if (theme === 'default' && extra === undefined) {
|
|
61
|
+
expect(result).toBe('');
|
|
62
|
+
}
|
|
63
|
+
// theme='default' with extra → result is exactly the extra string
|
|
64
|
+
if (theme === 'default' && extra !== undefined) {
|
|
65
|
+
expect(result).toBe(extra);
|
|
66
|
+
}
|
|
67
|
+
// result never contains both 'ux4g-footer-primary' and 'ux4g-footer-dark' simultaneously
|
|
68
|
+
const hasPrimary = result.includes('ux4g-footer-primary');
|
|
69
|
+
const hasDark = result.includes('ux4g-footer-dark');
|
|
70
|
+
expect(hasPrimary && hasDark).toBe(false);
|
|
71
|
+
// when extra provided, result contains extra token
|
|
72
|
+
if (extra !== undefined) {
|
|
73
|
+
expect(nonEmptyToks).toContain(extra);
|
|
74
|
+
}
|
|
75
|
+
// token array has no duplicates (filter out empty string tokens before checking)
|
|
76
|
+
expect(hasDuplicates(nonEmptyToks)).toBe(false);
|
|
77
|
+
// idempotent: calling twice with same inputs returns same string
|
|
78
|
+
const result2 = buildFooterClasses(theme, extra);
|
|
79
|
+
expect(result2).toBe(result);
|
|
80
|
+
}), { numRuns: 10 });
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
// ── Property 3: ResultListRow ─────────────────────────────────────────────────
|
|
84
|
+
// **Validates: Requirements 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.11, 3.15, 6.9**
|
|
85
|
+
describe('Property 3: ResultListRow class always contains base token and correctly toggles variation modifier', () => {
|
|
86
|
+
it('base token always present, variation modifier toggles correctly, mutually exclusive, no duplicates, extra appended, idempotent', () => {
|
|
87
|
+
fc.assert(fc.property(fc.constantFrom('default', 'v1', 'v2', 'v3', 'v4', 'v5'), fc.oneof(fc.constant(undefined), fc.stringMatching(/^[a-z][a-z0-9-]*$/).filter(s => s.length > 0 && s.length <= 20)), (variation, extra) => {
|
|
88
|
+
const result = buildResultListRowClasses(variation, extra);
|
|
89
|
+
const toks = tokens(result);
|
|
90
|
+
// result always contains 'ux4g-result-list' (base token)
|
|
91
|
+
expect(toks).toContain('ux4g-result-list');
|
|
92
|
+
// when variation='v1'–'v5' result contains 'ux4g-result-list-v{N}'
|
|
93
|
+
if (variation !== 'default') {
|
|
94
|
+
expect(toks).toContain(`ux4g-result-list-${variation}`);
|
|
95
|
+
}
|
|
96
|
+
// when variation='default' result contains no variation modifier class (none of v1-v5)
|
|
97
|
+
if (variation === 'default') {
|
|
98
|
+
expect(toks).not.toContain('ux4g-result-list-v1');
|
|
99
|
+
expect(toks).not.toContain('ux4g-result-list-v2');
|
|
100
|
+
expect(toks).not.toContain('ux4g-result-list-v3');
|
|
101
|
+
expect(toks).not.toContain('ux4g-result-list-v4');
|
|
102
|
+
expect(toks).not.toContain('ux4g-result-list-v5');
|
|
103
|
+
}
|
|
104
|
+
// result never contains more than one variation modifier class simultaneously
|
|
105
|
+
const variationModifiers = ['v1', 'v2', 'v3', 'v4', 'v5']
|
|
106
|
+
.map(v => `ux4g-result-list-${v}`)
|
|
107
|
+
.filter(cls => toks.includes(cls));
|
|
108
|
+
expect(variationModifiers.length).toBeLessThanOrEqual(1);
|
|
109
|
+
// when extra provided, result contains extra token
|
|
110
|
+
if (extra !== undefined) {
|
|
111
|
+
expect(toks).toContain(extra);
|
|
112
|
+
}
|
|
113
|
+
// token array has no duplicates
|
|
114
|
+
expect(hasDuplicates(toks)).toBe(false);
|
|
115
|
+
// idempotent: calling twice with same inputs returns same string
|
|
116
|
+
const result2 = buildResultListRowClasses(variation, extra);
|
|
117
|
+
expect(result2).toBe(result);
|
|
118
|
+
}), { numRuns: 10 });
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
// ── Property 4: Extra param across all Phase 10 build functions ───────────────
|
|
122
|
+
// **Validates: Requirements 1.4, 2.5, 3.8**
|
|
123
|
+
describe('Property 4: Extra param is always appended as a space-separated token and never duplicates existing tokens for all Phase 10 build functions', () => {
|
|
124
|
+
// Non-empty extra string with no spaces
|
|
125
|
+
const extraArb = fc.string({ minLength: 1 }).filter(s => !s.includes(' '));
|
|
126
|
+
it('buildSlotGridClasses: extra token is present exactly once and no duplicates', () => {
|
|
127
|
+
fc.assert(fc.property(fc.constantFrom('weekly', 'compact'), extraArb, (variant, extra) => {
|
|
128
|
+
const result = buildSlotGridClasses(variant, extra);
|
|
129
|
+
const toks = result.split(' ');
|
|
130
|
+
// extra token is present as a space-separated token
|
|
131
|
+
expect(toks).toContain(extra);
|
|
132
|
+
// extra token appears exactly once
|
|
133
|
+
const count = toks.filter(t => t === extra).length;
|
|
134
|
+
expect(count).toBe(1);
|
|
135
|
+
// token array has no duplicates
|
|
136
|
+
expect(hasDuplicates(toks)).toBe(false);
|
|
137
|
+
}), { numRuns: 10 });
|
|
138
|
+
});
|
|
139
|
+
it('buildFooterClasses: extra token is present exactly once and no duplicates', () => {
|
|
140
|
+
fc.assert(fc.property(fc.constantFrom('default', 'primary', 'dark'), extraArb, (theme, extra) => {
|
|
141
|
+
const result = buildFooterClasses(theme, extra);
|
|
142
|
+
// result is never empty when extra is provided
|
|
143
|
+
const toks = result.split(' ').filter(t => t !== '');
|
|
144
|
+
// extra token is present as a space-separated token
|
|
145
|
+
expect(toks).toContain(extra);
|
|
146
|
+
// extra token appears exactly once
|
|
147
|
+
const count = toks.filter(t => t === extra).length;
|
|
148
|
+
expect(count).toBe(1);
|
|
149
|
+
// token array has no duplicates
|
|
150
|
+
expect(hasDuplicates(toks)).toBe(false);
|
|
151
|
+
}), { numRuns: 10 });
|
|
152
|
+
});
|
|
153
|
+
it('buildResultListRowClasses: extra token is present exactly once and no duplicates', () => {
|
|
154
|
+
fc.assert(fc.property(fc.constantFrom('default', 'v1', 'v2', 'v3', 'v4', 'v5'), extraArb, (variation, extra) => {
|
|
155
|
+
const result = buildResultListRowClasses(variation, extra);
|
|
156
|
+
const toks = result.split(' ');
|
|
157
|
+
// extra token is present as a space-separated token
|
|
158
|
+
expect(toks).toContain(extra);
|
|
159
|
+
// extra token appears exactly once
|
|
160
|
+
const count = toks.filter(t => t === extra).length;
|
|
161
|
+
expect(count).toBe(1);
|
|
162
|
+
// token array has no duplicates
|
|
163
|
+
expect(hasDuplicates(toks)).toBe(false);
|
|
164
|
+
}), { numRuns: 10 });
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|