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.
Files changed (124) hide show
  1. package/README.md +76 -0
  2. package/dist/__tests__/css-bundle.integration.test.d.ts +11 -0
  3. package/dist/__tests__/css-bundle.integration.test.js +1102 -0
  4. package/dist/__tests__/css-bundle.phase10.property.test.d.ts +9 -0
  5. package/dist/__tests__/css-bundle.phase10.property.test.js +64 -0
  6. package/dist/__tests__/css-bundle.phase5.property.test.d.ts +9 -0
  7. package/dist/__tests__/css-bundle.phase5.property.test.js +126 -0
  8. package/dist/__tests__/css-bundle.phase6.property.test.d.ts +9 -0
  9. package/dist/__tests__/css-bundle.phase6.property.test.js +73 -0
  10. package/dist/__tests__/css-bundle.phase7.property.test.d.ts +9 -0
  11. package/dist/__tests__/css-bundle.phase7.property.test.js +76 -0
  12. package/dist/__tests__/css-bundle.phase8.property.test.d.ts +9 -0
  13. package/dist/__tests__/css-bundle.phase8.property.test.js +67 -0
  14. package/dist/__tests__/css-bundle.phase9.property.test.d.ts +9 -0
  15. package/dist/__tests__/css-bundle.phase9.property.test.js +93 -0
  16. package/dist/__tests__/css-bundle.property.test.d.ts +14 -0
  17. package/dist/__tests__/css-bundle.property.test.js +393 -0
  18. package/dist/__tests__/dom-generators.determinism.property.test.d.ts +1 -0
  19. package/dist/__tests__/dom-generators.determinism.property.test.js +71 -0
  20. package/dist/__tests__/dom-generators.id.property.test.d.ts +1 -0
  21. package/dist/__tests__/dom-generators.id.property.test.js +99 -0
  22. package/dist/__tests__/dom-generators.otp.property.test.d.ts +1 -0
  23. package/dist/__tests__/dom-generators.property.test.d.ts +1 -0
  24. package/dist/__tests__/dom-generators.property.test.js +205 -0
  25. package/dist/__tests__/dom-generators.states.property.test.d.ts +1 -0
  26. package/dist/__tests__/dom-generators.table.property.test.d.ts +1 -0
  27. package/dist/__tests__/dom-generators.tier1.property.test.d.ts +1 -0
  28. package/dist/__tests__/dom-generators.tier1.property.test.js +403 -0
  29. package/dist/__tests__/dom-generators.validation.property.test.d.ts +1 -0
  30. package/dist/__tests__/dom-generators.validation.property.test.js +327 -0
  31. package/dist/__tests__/megamenu.classbuilder.property.test.d.ts +1 -0
  32. package/dist/__tests__/megamenu.classbuilder.property.test.js +88 -0
  33. package/dist/__tests__/smoke.test.d.ts +1 -0
  34. package/dist/__tests__/smoke.test.js +65 -0
  35. package/dist/__tests__/types.phase10.property.test.d.ts +1 -0
  36. package/dist/__tests__/types.phase10.property.test.js +166 -0
  37. package/dist/__tests__/types.phase10.test.d.ts +1 -0
  38. package/dist/__tests__/types.phase10.test.js +76 -0
  39. package/dist/__tests__/types.phase3.property.test.d.ts +1 -0
  40. package/dist/__tests__/types.phase3.property.test.js +83 -0
  41. package/dist/__tests__/types.phase3.test.d.ts +1 -0
  42. package/dist/__tests__/types.phase3.test.js +76 -0
  43. package/dist/__tests__/types.phase4.property.test.d.ts +1 -0
  44. package/dist/__tests__/types.phase4.property.test.js +119 -0
  45. package/dist/__tests__/types.phase4.test.d.ts +1 -0
  46. package/dist/__tests__/types.phase4.test.js +70 -0
  47. package/dist/__tests__/types.phase5.property.test.d.ts +1 -0
  48. package/dist/__tests__/types.phase5.property.test.js +120 -0
  49. package/dist/__tests__/types.phase5.test.d.ts +1 -0
  50. package/dist/__tests__/types.phase5.test.js +64 -0
  51. package/dist/__tests__/types.phase6.property.test.d.ts +1 -0
  52. package/dist/__tests__/types.phase6.property.test.js +189 -0
  53. package/dist/__tests__/types.phase6.test.d.ts +1 -0
  54. package/dist/__tests__/types.phase6.test.js +121 -0
  55. package/dist/__tests__/types.phase7.property.test.d.ts +1 -0
  56. package/dist/__tests__/types.phase7.property.test.js +217 -0
  57. package/dist/__tests__/types.phase7.test.d.ts +1 -0
  58. package/dist/__tests__/types.phase7.test.js +106 -0
  59. package/dist/__tests__/types.phase8.property.test.d.ts +1 -0
  60. package/dist/__tests__/types.phase8.property.test.js +224 -0
  61. package/dist/__tests__/types.phase8.test.d.ts +1 -0
  62. package/dist/__tests__/types.phase8.test.js +114 -0
  63. package/dist/__tests__/types.phase9.property.test.d.ts +1 -0
  64. package/dist/__tests__/types.phase9.property.test.js +347 -0
  65. package/dist/__tests__/types.phase9.test.d.ts +1 -0
  66. package/dist/__tests__/types.phase9.test.js +226 -0
  67. package/dist/__tests__/types.restructure.property.test.d.ts +1 -0
  68. package/dist/__tests__/types.restructure.property.test.js +76 -0
  69. package/dist/__tests__/types.test.d.ts +1 -0
  70. package/dist/__tests__/types.test.js +175 -0
  71. package/dist/dom-generators/accordion.d.ts +23 -0
  72. package/dist/dom-generators/avatar.d.ts +19 -0
  73. package/dist/dom-generators/carousel.d.ts +20 -0
  74. package/dist/dom-generators/chip.d.ts +18 -0
  75. package/dist/dom-generators/combobox.d.ts +28 -0
  76. package/dist/dom-generators/date-picker.d.ts +19 -0
  77. package/dist/dom-generators/dom-generators/accordion.d.ts +21 -0
  78. package/dist/dom-generators/dom-generators/avatar.d.ts +17 -0
  79. package/dist/dom-generators/dom-generators/carousel.d.ts +19 -0
  80. package/dist/dom-generators/dom-generators/chip.d.ts +16 -0
  81. package/dist/dom-generators/dom-generators/combobox.d.ts +26 -0
  82. package/dist/dom-generators/dom-generators/date-picker.d.ts +18 -0
  83. package/dist/dom-generators/dom-generators/drawer.d.ts +17 -0
  84. package/dist/dom-generators/dom-generators/dropdown.d.ts +26 -0
  85. package/dist/dom-generators/dom-generators/file-upload.d.ts +20 -0
  86. package/dist/dom-generators/dom-generators/id-generator.d.ts +9 -0
  87. package/dist/dom-generators/dom-generators/index.d.ts +27 -0
  88. package/dist/dom-generators/dom-generators/modal.d.ts +19 -0
  89. package/dist/dom-generators/dom-generators/otp.d.ts +16 -0
  90. package/dist/dom-generators/dom-generators/popover.d.ts +17 -0
  91. package/dist/dom-generators/dom-generators/progress.d.ts +16 -0
  92. package/dist/dom-generators/dom-generators/search.d.ts +20 -0
  93. package/dist/dom-generators/dom-generators/stepper.d.ts +21 -0
  94. package/dist/dom-generators/dom-generators/table.d.ts +23 -0
  95. package/dist/dom-generators/dom-generators/tabs.d.ts +21 -0
  96. package/dist/dom-generators/dom-generators/time-picker.d.ts +18 -0
  97. package/dist/dom-generators/dom-generators/tooltip.d.ts +17 -0
  98. package/dist/dom-generators/dom-generators/types.d.ts +27 -0
  99. package/dist/dom-generators/dom-generators/validate.d.ts +20 -0
  100. package/dist/dom-generators/drawer.d.ts +19 -0
  101. package/dist/dom-generators/dropdown.d.ts +28 -0
  102. package/dist/dom-generators/file-upload.d.ts +22 -0
  103. package/dist/dom-generators/id-generator.d.ts +9 -0
  104. package/dist/dom-generators/index.bundled.d.ts +654 -0
  105. package/dist/dom-generators/index.cjs +2029 -0
  106. package/dist/dom-generators/index.d.ts +27 -0
  107. package/dist/dom-generators/index.mjs +2001 -0
  108. package/dist/dom-generators/modal.d.ts +21 -0
  109. package/dist/dom-generators/otp.d.ts +18 -0
  110. package/dist/dom-generators/popover.d.ts +19 -0
  111. package/dist/dom-generators/progress.d.ts +18 -0
  112. package/dist/dom-generators/search.d.ts +22 -0
  113. package/dist/dom-generators/stepper.d.ts +23 -0
  114. package/dist/dom-generators/table.d.ts +25 -0
  115. package/dist/dom-generators/tabs.d.ts +23 -0
  116. package/dist/dom-generators/time-picker.d.ts +19 -0
  117. package/dist/dom-generators/tooltip.d.ts +19 -0
  118. package/dist/dom-generators/types.d.ts +155 -0
  119. package/dist/dom-generators/validate.d.ts +20 -0
  120. package/dist/runtime/bootstrap.js +59 -0
  121. package/dist/runtime/index.js +55 -0
  122. package/dist/types.d.ts +155 -0
  123. package/dist/types.js +552 -0
  124. 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,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 {};