@pyreon/rocketstyle 0.24.4 → 0.24.6

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 (60) hide show
  1. package/package.json +8 -10
  2. package/src/__tests__/attrs-overloads.test.ts +0 -97
  3. package/src/__tests__/attrs.test.ts +0 -190
  4. package/src/__tests__/cache-key-boolean-collision.test.ts +0 -54
  5. package/src/__tests__/chaining.test.ts +0 -86
  6. package/src/__tests__/collection.test.ts +0 -35
  7. package/src/__tests__/compose.test.ts +0 -36
  8. package/src/__tests__/context.test.ts +0 -200
  9. package/src/__tests__/createLocalProvider.test.ts +0 -280
  10. package/src/__tests__/dimensions.test.ts +0 -183
  11. package/src/__tests__/e2e-styler.test.ts +0 -299
  12. package/src/__tests__/hooks.test.ts +0 -178
  13. package/src/__tests__/isRocketComponent.test.ts +0 -48
  14. package/src/__tests__/memo-cap.test.ts +0 -174
  15. package/src/__tests__/minimal-theme.test.ts +0 -62
  16. package/src/__tests__/misc.test.ts +0 -204
  17. package/src/__tests__/native-marker.test.ts +0 -9
  18. package/src/__tests__/providerConsumer.test.ts +0 -183
  19. package/src/__tests__/reactive-props-preservation.test.ts +0 -195
  20. package/src/__tests__/rocketstyle.browser.test.tsx +0 -481
  21. package/src/__tests__/rocketstyleIntegration.test.ts +0 -711
  22. package/src/__tests__/theme-integration.test.tsx +0 -254
  23. package/src/__tests__/themeUtils.test.ts +0 -463
  24. package/src/cache/LocalThemeManager.ts +0 -14
  25. package/src/cache/index.ts +0 -3
  26. package/src/constants/booleanTags.ts +0 -32
  27. package/src/constants/defaultDimensions.ts +0 -23
  28. package/src/constants/index.ts +0 -59
  29. package/src/context/context.ts +0 -70
  30. package/src/context/createLocalProvider.ts +0 -97
  31. package/src/context/localContext.ts +0 -37
  32. package/src/env.d.ts +0 -6
  33. package/src/hoc/index.ts +0 -3
  34. package/src/hoc/rocketstyleAttrsHoc.ts +0 -76
  35. package/src/hooks/index.ts +0 -4
  36. package/src/hooks/usePseudoState.ts +0 -79
  37. package/src/hooks/useTheme.ts +0 -48
  38. package/src/index.ts +0 -95
  39. package/src/init.ts +0 -93
  40. package/src/isRocketComponent.ts +0 -16
  41. package/src/rocketstyle.ts +0 -640
  42. package/src/types/attrs.ts +0 -23
  43. package/src/types/config.ts +0 -48
  44. package/src/types/configuration.ts +0 -69
  45. package/src/types/dimensions.ts +0 -109
  46. package/src/types/hoc.ts +0 -5
  47. package/src/types/pseudo.ts +0 -19
  48. package/src/types/rocketComponent.ts +0 -24
  49. package/src/types/rocketstyle.ts +0 -220
  50. package/src/types/styles.ts +0 -61
  51. package/src/types/theme.ts +0 -18
  52. package/src/types/utils.ts +0 -98
  53. package/src/utils/attrs.ts +0 -181
  54. package/src/utils/chaining.ts +0 -58
  55. package/src/utils/collection.ts +0 -9
  56. package/src/utils/compose.ts +0 -11
  57. package/src/utils/dimensions.ts +0 -126
  58. package/src/utils/statics.ts +0 -44
  59. package/src/utils/styles.ts +0 -18
  60. package/src/utils/theme.ts +0 -211
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/rocketstyle",
3
- "version": "0.24.4",
3
+ "version": "0.24.6",
4
4
  "description": "Multi-dimensional style composition for Pyreon components",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -13,8 +13,7 @@
13
13
  "!lib/**/*.map",
14
14
  "!lib/analysis",
15
15
  "README.md",
16
- "LICENSE",
17
- "src"
16
+ "LICENSE"
18
17
  ],
19
18
  "type": "module",
20
19
  "sideEffects": false,
@@ -22,7 +21,6 @@
22
21
  "types": "./lib/index.d.ts",
23
22
  "exports": {
24
23
  ".": {
25
- "bun": "./src/index.ts",
26
24
  "import": "./lib/index.js",
27
25
  "types": "./lib/index.d.ts"
28
26
  }
@@ -43,8 +41,8 @@
43
41
  },
44
42
  "devDependencies": {
45
43
  "@pyreon/test-utils": "^0.13.11",
46
- "@pyreon/typescript": "^0.24.4",
47
- "@pyreon/ui-core": "^0.24.4",
44
+ "@pyreon/typescript": "^0.24.6",
45
+ "@pyreon/ui-core": "^0.24.6",
48
46
  "@vitest/browser-playwright": "^4.1.4",
49
47
  "@vitus-labs/tools-rolldown": "^2.4.0"
50
48
  },
@@ -52,9 +50,9 @@
52
50
  "node": ">= 22"
53
51
  },
54
52
  "dependencies": {
55
- "@pyreon/core": "^0.24.4",
56
- "@pyreon/reactivity": "^0.24.4",
57
- "@pyreon/styler": "^0.24.4",
58
- "@pyreon/ui-core": "^0.24.4"
53
+ "@pyreon/core": "^0.24.6",
54
+ "@pyreon/reactivity": "^0.24.6",
55
+ "@pyreon/styler": "^0.24.6",
56
+ "@pyreon/ui-core": "^0.24.6"
59
57
  }
60
58
  }
@@ -1,97 +0,0 @@
1
- import type { ComponentFn } from '@pyreon/core'
2
- import rocketstyle from '../init'
3
-
4
- // Type-level regression tests for the post-#225/#227 `.attrs()` overload
5
- // split: (a) DFP widening makes `.attrs(obj)` keys optional at JSX call site,
6
- // (b) callback overload preserves Pyreon's loose-return convention so
7
- // `_documentProps` / `tag: 'a'` runtime extras still typecheck without
8
- // per-callsite `as any` casts.
9
- //
10
- // These are not bisect-load-bearing at runtime — they're type-level
11
- // assertions exercised by `tsc --noEmit`. Including them in the suite
12
- // makes failures show up in the test report (vitest treats type errors
13
- // as compile failures).
14
- describe('attrs overloads — type-level contract', () => {
15
- // A minimal base component standing in for Text / Button / etc.
16
- // We only care about the type-level surface here.
17
- type BaseProps = {
18
- tag?: 'div' | 'span' | 'p' | 'h1' | 'h2' | 'h3'
19
- role?: string
20
- }
21
- const Base: ComponentFn<BaseProps> = () => null
22
-
23
- describe('object overload — keys become optional at JSX site (PR #225)', () => {
24
- it('accepts object with default values', () => {
25
- const Comp = rocketstyle()({ name: 'Comp', component: Base }).attrs({
26
- tag: 'div',
27
- })
28
- // The component is callable — at the JSX call site, `tag` is now
29
- // optional because `.attrs({ tag: 'div' })` provides a default.
30
- // Pre-#225 the type would have required `tag` at the JSX site.
31
- // We can't directly assert via `expectTypeOf` without the dep, but
32
- // the smoke is: the chain compiles without errors.
33
- expect(typeof Comp).toBe('function')
34
- })
35
-
36
- it('accepts new keys in attrs object', () => {
37
- // The attrs object can introduce new keys beyond the base component's
38
- // props (here: `customField`). The keys flow into the returned
39
- // component's extended-attrs `EA` and become typed props.
40
- const Comp = rocketstyle()({ name: 'Comp', component: Base }).attrs({
41
- customField: 'hello',
42
- })
43
- expect(typeof Comp).toBe('function')
44
- })
45
- })
46
-
47
- describe('callback overload — Pyreon convention for runtime extras', () => {
48
- it('accepts callback returning fields outside the base prop union (no as-cast needed)', () => {
49
- // This is the canonical document-primitive pattern: a Text-based
50
- // rocketstyle that overrides `tag` to a value outside Text's strict
51
- // `tag` union AND adds a runtime-only `_documentProps` marker. The
52
- // callback's return type intentionally allows `Record<string, unknown>`
53
- // for keys outside the user's explicit `<P>` generic, matching
54
- // Pyreon's pre-#225 convention.
55
- const Comp = rocketstyle()({ name: 'DocLink', component: Base }).attrs<{
56
- href?: string
57
- }>((props) => ({
58
- tag: 'a', // 'a' is NOT in BaseProps['tag'] — falls through Record<string, unknown>
59
- _documentProps: { href: props.href ?? '#' },
60
- }))
61
- expect(typeof Comp).toBe('function')
62
- })
63
-
64
- it('accepts callback returning literal values (contextual narrowing via <P>)', () => {
65
- // When the user passes an explicit `<P>` generic, the callback's
66
- // return is contextually typed against `Partial<P>`. Writing
67
- // `tag: 'h1'` stays narrow at literal `'h1'` — no `as const` needed.
68
- // Note: tag is in BaseProps['tag'] union so this typechecks against
69
- // BOTH the wildcard arm AND the explicit P-key arm.
70
- const Comp = rocketstyle()({ name: 'Heading', component: Base }).attrs<{
71
- level?: number
72
- }>((props) => ({
73
- tag: `h${props.level ?? 1}` as 'h1' | 'h2' | 'h3',
74
- }))
75
- expect(typeof Comp).toBe('function')
76
- })
77
-
78
- it('callback receives full DFP-typed props for narrow reads', () => {
79
- // The `props` arg passed to the callback IS strictly typed as
80
- // `Partial<DFP & P>` — so reading `props.tag` narrows against the
81
- // wrapped component's full surface. This is the "props narrow,
82
- // return loose" asymmetry Pyreon settled on.
83
- const Comp = rocketstyle()({ name: 'Probe', component: Base }).attrs<{
84
- scale?: number
85
- }>((props) => {
86
- // Type check: `props.scale` is `number | undefined`, `props.tag`
87
- // is the narrow BaseProps['tag'] union | undefined.
88
- const _scale: number | undefined = props.scale
89
- const _tag: 'div' | 'span' | 'p' | 'h1' | 'h2' | 'h3' | undefined = props.tag
90
- expect(_scale).toBeUndefined()
91
- expect(_tag).toBeUndefined()
92
- return { scale: 1 }
93
- })
94
- expect(typeof Comp).toBe('function')
95
- })
96
- })
97
- })
@@ -1,190 +0,0 @@
1
- import {
2
- calculateChainOptions,
3
- calculateStylingAttrs,
4
- pickStyledAttrs,
5
- removeUndefinedProps,
6
- } from '../utils/attrs'
7
-
8
- describe('removeUndefinedProps', () => {
9
- it('removes keys with undefined values', () => {
10
- const result = removeUndefinedProps({ a: 1, b: undefined, c: 'hello' })
11
- expect(result).toEqual({ a: 1, c: 'hello' })
12
- })
13
-
14
- it('keeps null values', () => {
15
- const result = removeUndefinedProps({ a: null, b: 0 })
16
- expect(result).toEqual({ a: null, b: 0 })
17
- })
18
-
19
- it('keeps all falsy non-undefined values', () => {
20
- const result = removeUndefinedProps({ a: 0, b: '', c: false, d: null })
21
- expect(result).toEqual({ a: 0, b: '', c: false, d: null })
22
- })
23
-
24
- it('returns empty for all undefined', () => {
25
- expect(removeUndefinedProps({ a: undefined, b: undefined })).toEqual({})
26
- })
27
-
28
- it('returns empty for empty input', () => {
29
- expect(removeUndefinedProps({})).toEqual({})
30
- })
31
- })
32
-
33
- describe('pickStyledAttrs', () => {
34
- it('picks keys that exist in keywords with truthy values', () => {
35
- const result = pickStyledAttrs(
36
- { state: 'primary', size: 'large', label: 'hello' },
37
- { state: true, size: true },
38
- )
39
- expect(result).toEqual({ state: 'primary', size: 'large' })
40
- })
41
-
42
- it('ignores falsy prop values', () => {
43
- const result = pickStyledAttrs({ state: '', size: 'large' }, { state: true, size: true })
44
- expect(result).toEqual({ size: 'large' })
45
- })
46
-
47
- it('returns empty when no keywords match', () => {
48
- const result = pickStyledAttrs({ label: 'hello' } as any, { state: true })
49
- expect(result).toEqual({})
50
- })
51
-
52
- it('returns empty for empty props', () => {
53
- const result = pickStyledAttrs({}, { state: true })
54
- expect(result).toEqual({})
55
- })
56
- })
57
-
58
- describe('calculateChainOptions', () => {
59
- it('returns empty object when options is empty array', () => {
60
- const calc = calculateChainOptions([])
61
- expect(calc([])).toEqual({})
62
- })
63
-
64
- it('returns empty object when options is undefined', () => {
65
- const calc = calculateChainOptions(undefined)
66
- expect(calc([])).toEqual({})
67
- })
68
-
69
- it('evaluates chain of functions and merges via Object.assign', () => {
70
- const fn1 = (props: any) => ({ a: 1, ...props })
71
- const fn2 = (_props: any) => ({ b: 2 })
72
- const calc = calculateChainOptions([fn1, fn2])
73
- expect(calc([{ c: 3 }])).toEqual({ a: 1, b: 2, c: 3 })
74
- })
75
-
76
- it('later functions override earlier ones (shallow)', () => {
77
- const fn1 = () => ({ a: 1 })
78
- const fn2 = () => ({ a: 2 })
79
- const calc = calculateChainOptions([fn1, fn2])
80
- expect(calc([])).toEqual({ a: 2 })
81
- })
82
-
83
- it('passes all args to each function', () => {
84
- const fn = vi.fn(() => ({}))
85
- const calc = calculateChainOptions([fn])
86
- calc(['arg1', 'arg2'] as any)
87
- expect(fn).toHaveBeenCalledWith('arg1', 'arg2')
88
- })
89
- })
90
-
91
- describe('calculateStylingAttrs', () => {
92
- it('picks string values from props for dimensions', () => {
93
- const calc = calculateStylingAttrs({ useBooleans: false, multiKeys: {} })
94
- const result = calc({
95
- props: { state: 'primary', size: 'large' },
96
- dimensions: { state: {}, size: {} },
97
- })
98
- expect(result).toEqual({ state: 'primary', size: 'large' })
99
- })
100
-
101
- it('picks number values from props', () => {
102
- const calc = calculateStylingAttrs({ useBooleans: false, multiKeys: {} })
103
- const result = calc({
104
- props: { state: 0 },
105
- dimensions: { state: {} },
106
- })
107
- expect(result).toEqual({ state: 0 })
108
- })
109
-
110
- it('sets undefined for non-string/non-number values when booleans disabled', () => {
111
- const calc = calculateStylingAttrs({ useBooleans: false, multiKeys: {} })
112
- const result = calc({
113
- props: { state: true },
114
- dimensions: { state: {} },
115
- })
116
- expect(result).toEqual({ state: undefined })
117
- })
118
-
119
- it('allows arrays for multi-key dimensions', () => {
120
- const calc = calculateStylingAttrs({
121
- useBooleans: false,
122
- multiKeys: { multiple: true },
123
- })
124
- const result = calc({
125
- props: { multiple: ['a', 'b'] },
126
- dimensions: { multiple: {} },
127
- })
128
- expect(result).toEqual({ multiple: ['a', 'b'] })
129
- })
130
-
131
- it('resolves boolean props when useBooleans is true (single key)', () => {
132
- const calc = calculateStylingAttrs({
133
- useBooleans: true,
134
- multiKeys: {},
135
- })
136
- const result = calc({
137
- props: { primary: true },
138
- dimensions: { state: { primary: true, secondary: true } },
139
- })
140
- expect(result).toEqual({ state: 'primary' })
141
- })
142
-
143
- it('resolves multi-key boolean props as array', () => {
144
- const calc = calculateStylingAttrs({
145
- useBooleans: true,
146
- multiKeys: { multiple: true },
147
- })
148
- const result = calc({
149
- props: { a: true, b: true },
150
- dimensions: { multiple: { a: true, b: true, c: true } },
151
- })
152
- expect(result.multiple).toEqual(expect.arrayContaining(['a', 'b']))
153
- })
154
-
155
- it('prefers explicit string prop over boolean shorthand', () => {
156
- const calc = calculateStylingAttrs({
157
- useBooleans: true,
158
- multiKeys: {},
159
- })
160
- const result = calc({
161
- props: { state: 'secondary', primary: true },
162
- dimensions: { state: { primary: true, secondary: true } },
163
- })
164
- expect(result).toEqual({ state: 'secondary' })
165
- })
166
-
167
- it('skips boolean keyword when prop value is falsy', () => {
168
- const calc = calculateStylingAttrs({
169
- useBooleans: true,
170
- multiKeys: {},
171
- })
172
- const result = calc({
173
- props: { primary: false },
174
- dimensions: { state: { primary: true, secondary: true } },
175
- })
176
- expect(result.state).toBeUndefined()
177
- })
178
-
179
- it('skips boolean resolution when value is already set', () => {
180
- const calc = calculateStylingAttrs({
181
- useBooleans: true,
182
- multiKeys: {},
183
- })
184
- const result = calc({
185
- props: { state: 'primary', secondary: true },
186
- dimensions: { state: { primary: true, secondary: true } },
187
- })
188
- expect(result.state).toBe('primary')
189
- })
190
- })
@@ -1,54 +0,0 @@
1
- /**
2
- * Bug reproduction: under `useBooleans: true`, `_resolveRsEntry`'s cache
3
- * key reads `propsRec[dimName]` directly (e.g. `propsRec.state`). Boolean
4
- * shorthand props like `<X primary />` populate `propsRec.primary` (NOT
5
- * `propsRec.state`), so the cache key for the `state` slot is `undefined`
6
- * → `''` regardless of which boolean variant was passed.
7
- *
8
- * Result: `<X primary />` and `<X secondary />` produce identical cache
9
- * keys and share the cached entry. The first-resolved variant's
10
- * `$rocketstyle` wins for all subsequent renders.
11
- */
12
- import { initTestConfig, withThemeContext } from '@pyreon/test-utils'
13
- import rocketstyle from '../init'
14
-
15
- let cleanup: () => void
16
- beforeAll(() => {
17
- cleanup = initTestConfig()
18
- })
19
- afterAll(() => cleanup())
20
-
21
- const ThemeCapture: any = ({ $rocketstyle, $rocketstate, ...rest }: any) => ({
22
- type: 'div',
23
- props: rest,
24
- $rocketstyle: typeof $rocketstyle === 'function' ? $rocketstyle() : $rocketstyle,
25
- $rocketstate: typeof $rocketstate === 'function' ? $rocketstate() : $rocketstate,
26
- })
27
- ThemeCapture.displayName = 'ThemeCapture'
28
-
29
- describe('rocketstyle — cache-key collision under useBooleans:true', () => {
30
- it('different boolean variants produce different $rocketstyle (NOT collide)', () => {
31
- const Button: any = rocketstyle({ useBooleans: true })({
32
- name: 'BoolButton',
33
- component: ThemeCapture,
34
- }).states(() => ({
35
- primary: { color: 'red' },
36
- secondary: { color: 'blue' },
37
- }))
38
-
39
- // Render with primary=true. Captures the $rocketstyle resolved
40
- // for state='primary'.
41
- const a = withThemeContext(() => Button({ primary: true }))
42
-
43
- // Render with secondary=true. Should resolve to state='secondary'
44
- // and produce DIFFERENT $rocketstyle.
45
- const b = withThemeContext(() => Button({ secondary: true }))
46
-
47
- // Bug: a.$rocketstyle === b.$rocketstyle (same cached entry).
48
- // Fix: a.$rocketstyle.color === 'red', b.$rocketstyle.color === 'blue'.
49
- expect(a.$rocketstate.state).toBe('primary')
50
- expect(b.$rocketstate.state).toBe('secondary')
51
- expect(a.$rocketstyle.color).toBe('red')
52
- expect(b.$rocketstyle.color).toBe('blue')
53
- })
54
- })
@@ -1,86 +0,0 @@
1
- import { chainOptions, chainOrOptions, chainReservedKeyOptions } from '../utils/chaining'
2
-
3
- describe('chainOptions', () => {
4
- it('appends function to defaults', () => {
5
- const fn1 = () => ({ a: 1 })
6
- const fn2 = () => ({ b: 2 })
7
- const result = chainOptions(fn2, [fn1])
8
- expect(result).toHaveLength(2)
9
- expect(result[0]).toBe(fn1)
10
- expect(result[1]).toBe(fn2)
11
- })
12
-
13
- it('wraps object in function and appends', () => {
14
- const obj = { a: 1 }
15
- const result = chainOptions(obj, [])
16
- expect(result).toHaveLength(1)
17
- expect(result[0]?.()).toEqual({ a: 1 })
18
- })
19
-
20
- it('returns defaults when opts is undefined', () => {
21
- const fn1 = () => ({ a: 1 })
22
- const result = chainOptions(undefined, [fn1])
23
- expect(result).toEqual([fn1])
24
- })
25
-
26
- it('handles empty defaults', () => {
27
- const fn = () => ({ a: 1 })
28
- const result = chainOptions(fn, [])
29
- expect(result).toHaveLength(1)
30
- })
31
-
32
- it('defaults to empty array when defaultOpts missing', () => {
33
- const fn = () => ({ a: 1 })
34
- // @ts-expect-error testing with undefined defaults
35
- const result = chainOptions(fn, undefined)
36
- expect(result).toHaveLength(1)
37
- })
38
- })
39
-
40
- describe('chainOrOptions', () => {
41
- it('merges opts with defaults using keys', () => {
42
- const keys = ['a', 'b', 'c'] as const
43
- const opts = { a: 'new', c: 'also' }
44
- const defaults = { a: 'old', b: 'default', c: 'orig' }
45
- const result = chainOrOptions(keys, opts, defaults)
46
- expect(result).toEqual({ a: 'new', b: 'default', c: 'also' })
47
- })
48
-
49
- it('uses default when opt is falsy', () => {
50
- const keys = ['a'] as const
51
- const opts = { a: '' }
52
- const defaults = { a: 'default' }
53
- const result = chainOrOptions(keys, opts, defaults)
54
- expect(result).toEqual({ a: 'default' })
55
- })
56
-
57
- it('handles missing keys in both', () => {
58
- const keys = ['x'] as const
59
- const result = chainOrOptions(keys, {}, {})
60
- expect(result).toEqual({ x: undefined })
61
- })
62
- })
63
-
64
- describe('chainReservedKeyOptions', () => {
65
- it('chains options for each reserved key', () => {
66
- const keys = ['theme', 'styles'] as const
67
- const fn1 = () => ({ a: 1 })
68
- const fn2 = () => ({ b: 2 })
69
- const opts = { theme: fn2 }
70
- const defaults = { theme: [fn1], styles: [] }
71
-
72
- const result = chainReservedKeyOptions(keys, opts, defaults)
73
- expect(result.theme).toHaveLength(2)
74
- expect(result.styles).toHaveLength(0)
75
- })
76
-
77
- it('wraps object opts into functions', () => {
78
- const keys = ['theme'] as const
79
- const opts = { theme: { color: 'red' } }
80
- const defaults = { theme: [] }
81
-
82
- const result = chainReservedKeyOptions(keys, opts, defaults)
83
- expect(result.theme).toHaveLength(1)
84
- expect(result.theme?.[0]?.()).toEqual({ color: 'red' })
85
- })
86
- })
@@ -1,35 +0,0 @@
1
- import { removeNullableValues } from '../utils/collection'
2
-
3
- describe('removeNullableValues', () => {
4
- it('removes null values', () => {
5
- expect(removeNullableValues({ a: 1, b: null })).toEqual({ a: 1 })
6
- })
7
-
8
- it('removes undefined values', () => {
9
- expect(removeNullableValues({ a: 1, b: undefined })).toEqual({ a: 1 })
10
- })
11
-
12
- it('removes false values', () => {
13
- expect(removeNullableValues({ a: 1, b: false })).toEqual({ a: 1 })
14
- })
15
-
16
- it('keeps truthy values', () => {
17
- expect(removeNullableValues({ a: 1, b: 'hello', c: true })).toEqual({
18
- a: 1,
19
- b: 'hello',
20
- c: true,
21
- })
22
- })
23
-
24
- it('keeps zero and empty string', () => {
25
- expect(removeNullableValues({ a: 0, b: '' })).toEqual({ a: 0, b: '' })
26
- })
27
-
28
- it('returns empty object for all nullable', () => {
29
- expect(removeNullableValues({ a: null, b: undefined, c: false })).toEqual({})
30
- })
31
-
32
- it('handles empty object', () => {
33
- expect(removeNullableValues({})).toEqual({})
34
- })
35
- })
@@ -1,36 +0,0 @@
1
- import { calculateHocsFuncs } from '../utils/compose'
2
-
3
- describe('calculateHocsFuncs', () => {
4
- it('extracts functions from object values', () => {
5
- const fn1 = (x: any) => x
6
- const fn2 = (x: any) => x
7
- const options = { a: fn1, b: fn2 }
8
- const result = calculateHocsFuncs(options)
9
- expect(result).toHaveLength(2)
10
- })
11
-
12
- it('filters out non-function values', () => {
13
- const fn = (x: any) => x
14
- const options = { a: fn, b: 'string', c: 42, d: null }
15
- const result = calculateHocsFuncs(options)
16
- expect(result).toHaveLength(1)
17
- expect(result[0]).toBe(fn)
18
- })
19
-
20
- it('reverses the order', () => {
21
- const fn1 = () => 'first'
22
- const fn2 = () => 'second'
23
- const options = { a: fn1, b: fn2 }
24
- const result = calculateHocsFuncs(options)
25
- expect(result[0]).toBe(fn2)
26
- expect(result[1]).toBe(fn1)
27
- })
28
-
29
- it('returns empty array for empty options', () => {
30
- expect(calculateHocsFuncs({})).toEqual([])
31
- })
32
-
33
- it('handles undefined options', () => {
34
- expect(calculateHocsFuncs(undefined as any)).toEqual([])
35
- })
36
- })