@tehw0lf/yaft 0.0.15 → 0.0.17

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 (51) hide show
  1. package/FeatureToggle.d.ts +20 -0
  2. package/FeatureToggle.js +72 -0
  3. package/README.md +25 -3
  4. package/evaluate.d.ts +42 -0
  5. package/evaluate.js +102 -0
  6. package/examples/ApiServiceBooleanProvider.d.ts +11 -0
  7. package/examples/ApiServiceBooleanProvider.js +85 -0
  8. package/examples/ApiServiceFeatureProvider.d.ts +17 -0
  9. package/examples/ApiServiceFeatureProvider.js +54 -0
  10. package/examples/LocalStorageBooleanProvider.d.ts +7 -0
  11. package/examples/LocalStorageBooleanProvider.js +23 -0
  12. package/examples/LocalStorageFeatureProvider.d.ts +14 -0
  13. package/examples/LocalStorageFeatureProvider.js +31 -0
  14. package/index.d.ts +3 -0
  15. package/index.js +14 -0
  16. package/mapping.d.ts +35 -0
  17. package/mapping.js +98 -0
  18. package/package.json +21 -9
  19. package/.github/workflows/build.yml +0 -28
  20. package/.github/workflows/security-scan.yml +0 -79
  21. package/CLAUDE.md +0 -151
  22. package/LICENSE +0 -21
  23. package/conformance.lock +0 -2
  24. package/jest.config.js +0 -16
  25. package/scripts/fetch-conformance.sh +0 -67
  26. package/src/FeatureToggle.ts +0 -95
  27. package/src/evaluate.ts +0 -122
  28. package/src/examples/ApiServiceBooleanProvider.ts +0 -82
  29. package/src/examples/ApiServiceFeatureProvider.ts +0 -52
  30. package/src/examples/LocalStorageBooleanProvider.ts +0 -25
  31. package/src/examples/LocalStorageFeatureProvider.ts +0 -31
  32. package/src/index.ts +0 -8
  33. package/src/mapping.ts +0 -92
  34. package/src/test/conformance-adapter/cases.ts +0 -104
  35. package/src/test/conformance-adapter/decorator.spec.ts +0 -376
  36. package/src/test/conformance-adapter/evaluation.spec.ts +0 -31
  37. package/src/test/conformance-adapter/mapping.spec.ts +0 -79
  38. package/src/test/decorator-behavior.spec.ts +0 -359
  39. package/src/test/error-handling.spec.ts +0 -396
  40. package/src/test/evaluate.spec.ts +0 -260
  41. package/src/test/feature-data.spec.ts +0 -102
  42. package/src/test/injectable-clock.spec.ts +0 -112
  43. package/src/test/integration.spec.ts +0 -472
  44. package/src/test/providers.spec.ts +0 -218
  45. package/src/test/test-boolean.json +0 -1
  46. package/src/test/test-feature.json +0 -20
  47. package/src/test/test-setup.ts +0 -2
  48. package/src/test/time-based-logic.spec.ts +0 -169
  49. package/src/test/yaft.demo.spec.ts +0 -53
  50. package/tsconfig.json +0 -14
  51. package/tsconfig.spec.json +0 -10
@@ -1,359 +0,0 @@
1
- import { FeatureToggle, FeatureToggleBase, FeatureProvider } from '../FeatureToggle';
2
-
3
- describe('FeatureToggle Decorator Behavior', () => {
4
- let mockProvider: FeatureProvider<boolean>;
5
-
6
- beforeEach(() => {
7
- mockProvider = {
8
- data: {},
9
- getConfig: jest.fn(),
10
- isEnabled: jest.fn()
11
- };
12
- FeatureToggleBase.featureProvider = mockProvider;
13
- });
14
-
15
- afterEach(() => {
16
- jest.clearAllMocks();
17
- });
18
-
19
- describe('Method Decoration', () => {
20
- it('should execute original method when feature is enabled', () => {
21
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(true);
22
- let executionResult = '';
23
-
24
- class TestClass {
25
- @FeatureToggle('enabledFeature')
26
- testMethod() {
27
- executionResult = 'original method executed';
28
- return 'original result';
29
- }
30
- }
31
-
32
- const instance = new TestClass();
33
- const result = instance.testMethod();
34
-
35
- expect(executionResult).toBe('original method executed');
36
- expect(result).toBe('original result');
37
- expect(mockProvider.isEnabled).toHaveBeenCalledWith('enabledFeature');
38
- });
39
-
40
- it('should execute fallback method when feature is disabled', () => {
41
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(false);
42
- let executionResult = '';
43
-
44
- function fallbackMethod() {
45
- executionResult = 'fallback method executed';
46
- return 'fallback result';
47
- }
48
-
49
- class TestClass {
50
- @FeatureToggle('disabledFeature', fallbackMethod)
51
- testMethod() {
52
- executionResult = 'original method executed';
53
- return 'original result';
54
- }
55
- }
56
-
57
- const instance = new TestClass();
58
- const result = instance.testMethod();
59
-
60
- expect(executionResult).toBe('fallback method executed');
61
- expect(result).toBe('fallback result');
62
- expect(mockProvider.isEnabled).toHaveBeenCalledWith('disabledFeature');
63
- });
64
-
65
- it('should execute empty function when feature is disabled and no fallback provided', () => {
66
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(false);
67
- let executionResult = '';
68
-
69
- class TestClass {
70
- @FeatureToggle('disabledFeature')
71
- testMethod() {
72
- executionResult = 'original method executed';
73
- return 'original result';
74
- }
75
- }
76
-
77
- const instance = new TestClass();
78
- const result = instance.testMethod();
79
-
80
- expect(executionResult).toBe('');
81
- expect(result).toBeUndefined();
82
- });
83
-
84
- it('should preserve method context (this binding)', () => {
85
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(true);
86
-
87
- class TestClass {
88
- value = 'test value';
89
-
90
- @FeatureToggle('enabledFeature')
91
- getValue() {
92
- return this.value;
93
- }
94
- }
95
-
96
- const instance = new TestClass();
97
- const result = instance.getValue();
98
-
99
- expect(result).toBe('test value');
100
- });
101
-
102
- it('should handle method arguments correctly', () => {
103
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(true);
104
-
105
- class TestClass {
106
- @FeatureToggle('enabledFeature')
107
- add(a: number, b: number) {
108
- return a + b;
109
- }
110
- }
111
-
112
- const instance = new TestClass();
113
- const result = instance.add(5, 3);
114
-
115
- expect(result).toBe(8);
116
- });
117
-
118
- it('should handle async methods', async () => {
119
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(true);
120
-
121
- class TestClass {
122
- @FeatureToggle('enabledFeature')
123
- async asyncMethod() {
124
- return Promise.resolve('async result');
125
- }
126
- }
127
-
128
- const instance = new TestClass();
129
- const result = await instance.asyncMethod();
130
-
131
- expect(result).toBe('async result');
132
- });
133
-
134
- it('should handle async fallback methods', async () => {
135
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(false);
136
-
137
- async function asyncFallback() {
138
- return Promise.resolve('async fallback result');
139
- }
140
-
141
- class TestClass {
142
- @FeatureToggle('disabledFeature', asyncFallback)
143
- async asyncMethod() {
144
- return Promise.resolve('async original result');
145
- }
146
- }
147
-
148
- const instance = new TestClass();
149
- const result = await instance.asyncMethod();
150
-
151
- expect(result).toBe('async fallback result');
152
- });
153
- });
154
-
155
- describe('Class Decoration', () => {
156
- it('should use original class when feature is enabled', () => {
157
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(true);
158
-
159
- @FeatureToggle('enabledFeature')
160
- class TestClass {
161
- value = 'original class';
162
-
163
- getValue() {
164
- return this.value;
165
- }
166
- }
167
-
168
- const instance = new TestClass();
169
- expect(instance.getValue()).toBe('original class');
170
- expect(instance instanceof TestClass).toBe(true);
171
- });
172
-
173
- it('should use fallback class when feature is disabled', () => {
174
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(false);
175
-
176
- class FallbackClass {
177
- value = 'fallback class';
178
-
179
- getValue() {
180
- return this.value;
181
- }
182
- }
183
-
184
- @FeatureToggle('disabledFeature', FallbackClass)
185
- class TestClass {
186
- value = 'original class';
187
-
188
- getValue() {
189
- return this.value;
190
- }
191
- }
192
-
193
- const instance = new TestClass();
194
- expect(instance.getValue()).toBe('fallback class');
195
- });
196
-
197
- it('should use EmptyClass when feature is disabled and no fallback provided', () => {
198
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(false);
199
-
200
- @FeatureToggle('disabledFeature')
201
- class TestClass {
202
- value = 'original class';
203
-
204
- getValue() {
205
- return this.value;
206
- }
207
-
208
- async getAsyncValue() {
209
- return Promise.resolve(this.value);
210
- }
211
- }
212
-
213
- const instance = new TestClass();
214
-
215
- // Should have empty methods
216
- expect(instance.getValue()).toBeUndefined();
217
-
218
- // Should handle async methods
219
- expect(instance.getAsyncValue()).toBeInstanceOf(Promise);
220
- });
221
-
222
- it('should preserve constructor arguments in fallback class', () => {
223
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(false);
224
-
225
- class FallbackClass {
226
- constructor(public name: string, public age: number) {}
227
-
228
- getInfo() {
229
- return `${this.name} is ${this.age}`;
230
- }
231
- }
232
-
233
- @FeatureToggle('disabledFeature', FallbackClass)
234
- class TestClass {
235
- constructor(public name: string, public age: number) {}
236
-
237
- getInfo() {
238
- return `Original: ${this.name} is ${this.age}`;
239
- }
240
- }
241
-
242
- const instance = new TestClass('John', 30);
243
- expect(instance.getInfo()).toBe('John is 30');
244
- });
245
-
246
- it('should handle inheritance in fallback classes', () => {
247
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(false);
248
-
249
- class BaseClass {
250
- baseMethod() {
251
- return 'base method';
252
- }
253
- }
254
-
255
- class FallbackClass extends BaseClass {
256
- fallbackMethod() {
257
- return 'fallback method';
258
- }
259
- }
260
-
261
- @FeatureToggle('disabledFeature', FallbackClass)
262
- class TestClass {
263
- originalMethod() {
264
- return 'original method';
265
- }
266
- }
267
-
268
- const instance = new TestClass();
269
- expect((instance as any).baseMethod()).toBe('base method');
270
- expect((instance as any).fallbackMethod()).toBe('fallback method');
271
- });
272
- });
273
-
274
- describe('Edge Cases and Error Conditions', () => {
275
- it('should throw error when FeatureToggleProvider is not set', () => {
276
- FeatureToggleBase.featureProvider = null as any;
277
-
278
- expect(() => {
279
- class TestClass {
280
- @FeatureToggle('anyFeature')
281
- testMethod() {}
282
- }
283
- }).toThrow('FeatureToggleProvider not set');
284
- });
285
-
286
- it('should handle provider throwing errors gracefully', () => {
287
- (mockProvider.isEnabled as jest.Mock).mockImplementation(() => {
288
- throw new Error('Provider error');
289
- });
290
-
291
- class TestClass {
292
- @FeatureToggle('errorFeature')
293
- testMethod() {}
294
- }
295
-
296
- expect(() => new TestClass().testMethod()).toThrow('Provider error');
297
- });
298
-
299
- it('should handle undefined/null feature keys', () => {
300
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(false);
301
-
302
- class TestClass {
303
- @FeatureToggle(undefined as any)
304
- testMethod() {
305
- return 'should not execute';
306
- }
307
- }
308
-
309
- const instance = new TestClass();
310
- const result = instance.testMethod();
311
-
312
- expect(result).toBeUndefined();
313
- expect(mockProvider.isEnabled).toHaveBeenCalledWith(undefined);
314
- });
315
-
316
- it('should handle empty string feature keys', () => {
317
- (mockProvider.isEnabled as jest.Mock).mockReturnValue(false);
318
-
319
- class TestClass {
320
- @FeatureToggle('')
321
- testMethod() {
322
- return 'should not execute';
323
- }
324
- }
325
-
326
- const instance = new TestClass();
327
- const result = instance.testMethod();
328
-
329
- expect(result).toBeUndefined();
330
- expect(mockProvider.isEnabled).toHaveBeenCalledWith('');
331
- });
332
- });
333
-
334
- describe('Multiple Decorators on Same Class/Method', () => {
335
- it('should handle multiple method decorators', () => {
336
- (mockProvider.isEnabled as jest.Mock)
337
- .mockReturnValueOnce(true) // First feature enabled
338
- .mockReturnValueOnce(false); // Second feature disabled
339
-
340
- function fallback() {
341
- return 'fallback executed';
342
- }
343
-
344
- class TestClass {
345
- @FeatureToggle('feature1')
346
- @FeatureToggle('feature2', fallback)
347
- testMethod() {
348
- return 'original executed';
349
- }
350
- }
351
-
352
- const instance = new TestClass();
353
- const result = instance.testMethod();
354
-
355
- // The behavior depends on decorator execution order
356
- expect(mockProvider.isEnabled).toHaveBeenCalledTimes(2);
357
- });
358
- });
359
- });