@pyreon/connector-document 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.
@@ -1,548 +0,0 @@
1
- import { h } from '@pyreon/core'
2
- import { describe, expect, it } from 'vitest'
3
- import type { DocumentMarker } from '../extractDocumentTree'
4
- import { extractDocumentTree } from '../extractDocumentTree'
5
-
6
- // Helper: build a real VNode via @pyreon/core's h(). The third arg
7
- // is an array (kept for parity with the prior mock helper) and
8
- // spreads into h()'s varargs. All tests below run real-h() VNodes
9
- // through the extraction pipeline — see PR #197 for why mock-only
10
- // tests masked a silent metadata drop in the real attrs HOC path.
11
- const node = (
12
- type: string | ((...args: any[]) => any),
13
- props: Record<string, any> = {},
14
- children: unknown[] = [],
15
- ) => h(type as any, props, ...(children as any[])) as any
16
-
17
- // Helper: create a document-marked component function
18
- const docComponent = (docType: string, render?: (...args: any[]) => any) => {
19
- const fn = render ?? ((props: any) => node('div', props, props.children ? [props.children] : []))
20
- ;(fn as any)._documentType = docType
21
- return fn as ((...args: any[]) => any) & DocumentMarker
22
- }
23
-
24
- describe('extractDocumentTree', () => {
25
- it('extracts a simple document node', () => {
26
- const Heading = docComponent('heading')
27
- const tree = node(
28
- Heading,
29
- { $rocketstyle: { fontSize: 24, fontWeight: 'bold' }, _documentProps: { level: 1 } },
30
- ['Hello World'],
31
- )
32
-
33
- const result = extractDocumentTree(tree)
34
-
35
- expect(result.type).toBe('heading')
36
- expect(result.props).toEqual({ level: 1 })
37
- expect(result.children).toEqual(['Hello World'])
38
- expect(result.styles).toEqual({ fontSize: 24, fontWeight: 'bold' })
39
- })
40
-
41
- it('extracts nested document nodes', () => {
42
- const Section = docComponent('section')
43
- const Text = docComponent('text')
44
-
45
- const tree = node(Section, { $rocketstyle: { padding: 16 } }, [
46
- node(Text, { $rocketstyle: { fontSize: 14, color: '#333' } }, ['Paragraph one']),
47
- node(Text, { $rocketstyle: { fontSize: 14, color: '#333' } }, ['Paragraph two']),
48
- ])
49
-
50
- const result = extractDocumentTree(tree)
51
-
52
- expect(result.type).toBe('section')
53
- expect(result.styles).toEqual({ padding: 16 })
54
- expect(result.children).toHaveLength(2)
55
- expect((result.children[0] as any).type).toBe('text')
56
- expect((result.children[1] as any).type).toBe('text')
57
- })
58
-
59
- it('flattens transparent wrappers', () => {
60
- const Section = docComponent('section')
61
- const Text = docComponent('text')
62
-
63
- // A plain div wrapper (no _documentType) should be transparent
64
- const tree = node(Section, {}, [
65
- node('div', {}, [node(Text, { $rocketstyle: { fontSize: 14 } }, ['Hello'])]),
66
- ])
67
-
68
- const result = extractDocumentTree(tree)
69
-
70
- expect(result.type).toBe('section')
71
- expect(result.children).toHaveLength(1)
72
- expect((result.children[0] as any).type).toBe('text')
73
- })
74
-
75
- it('handles string children', () => {
76
- const Text = docComponent('text')
77
- const tree = node(Text, {}, ['Hello', ' ', 'World'])
78
-
79
- const result = extractDocumentTree(tree)
80
-
81
- expect(result.children).toEqual(['Hello', ' ', 'World'])
82
- })
83
-
84
- it('handles number children', () => {
85
- const Text = docComponent('text')
86
- const tree = node(Text, {}, [42])
87
-
88
- const result = extractDocumentTree(tree)
89
-
90
- expect(result.children).toEqual(['42'])
91
- })
92
-
93
- it('skips null and boolean children', () => {
94
- const Section = docComponent('section')
95
- const tree = node(Section, {}, [null, false, true, 'visible'])
96
-
97
- const result = extractDocumentTree(tree)
98
-
99
- expect(result.children).toEqual(['visible'])
100
- })
101
-
102
- it('resolves reactive getter children', () => {
103
- const Text = docComponent('text')
104
- const tree = node(Text, {}, [() => 'dynamic text'])
105
-
106
- const result = extractDocumentTree(tree)
107
-
108
- expect(result.children).toEqual(['dynamic text'])
109
- })
110
-
111
- it('omits styles when includeStyles is false', () => {
112
- const Heading = docComponent('heading')
113
- const tree = node(Heading, { $rocketstyle: { fontSize: 24 } }, ['Hello'])
114
-
115
- const result = extractDocumentTree(tree, { includeStyles: false })
116
-
117
- expect(result.styles).toBeUndefined()
118
- })
119
-
120
- it('wraps in document node when root has no _documentType', () => {
121
- const tree = node('div', {}, ['raw text'])
122
-
123
- const result = extractDocumentTree(tree)
124
-
125
- expect(result.type).toBe('document')
126
- expect(result.children).toEqual(['raw text'])
127
- })
128
-
129
- it('handles component functions without _documentType by calling them', () => {
130
- const Text = docComponent('text')
131
- const Wrapper = (props: any) =>
132
- node(Text, { $rocketstyle: { fontSize: 14 } }, [props.children])
133
-
134
- const tree = node(Wrapper, {}, ['wrapped text'])
135
-
136
- const result = extractDocumentTree(tree)
137
-
138
- expect(result.type).toBe('text')
139
- expect(result.children).toEqual(['wrapped text'])
140
- })
141
-
142
- it('handles function passed directly', () => {
143
- const Text = docComponent('text')
144
- const template = () => node(Text, { $rocketstyle: { fontSize: 14 } }, ['Hello'])
145
-
146
- const result = extractDocumentTree(template)
147
-
148
- expect(result.type).toBe('text')
149
- expect(result.children).toEqual(['Hello'])
150
- })
151
-
152
- it('creates empty document for null input', () => {
153
- const result = extractDocumentTree(null)
154
-
155
- expect(result.type).toBe('document')
156
- expect(result.children).toEqual([])
157
- })
158
-
159
- describe('_documentProps function value resolution (D1)', () => {
160
- // Document primitives like DocDocument now accept reactive
161
- // accessors (e.g. `title={() => store.name()}`) and store the
162
- // function in _documentProps. extractDocumentTree resolves the
163
- // function at extraction time so the export pipeline always
164
- // sees the live value, not a stale snapshot from component
165
- // mount time.
166
- //
167
- // These tests lock in the behavior at the connector-document
168
- // boundary so any future change to the resolution logic gets
169
- // caught by a focused unit test.
170
-
171
- it('calls function values in _documentProps and stores the result', () => {
172
- const Document = docComponent('document')
173
- const tree = node(
174
- Document,
175
- {
176
- _documentProps: {
177
- title: () => 'Resolved title',
178
- author: () => 'Alice',
179
- subject: 'Plain string still works',
180
- },
181
- },
182
- [],
183
- )
184
-
185
- const result = extractDocumentTree(tree)
186
-
187
- expect(result.props).toEqual({
188
- title: 'Resolved title',
189
- author: 'Alice',
190
- subject: 'Plain string still works',
191
- })
192
- })
193
-
194
- it('reads the live value each time extractDocumentTree is called', () => {
195
- // The whole point of accessors: every export call sees the
196
- // current state, not a value frozen at component mount.
197
- // We simulate this by mutating the closure variable between
198
- // two extractDocumentTree calls on the same vnode.
199
- let counter = 0
200
- const Document = docComponent('document')
201
- const tree = node(
202
- Document,
203
- {
204
- _documentProps: {
205
- title: () => `Export ${++counter}`,
206
- },
207
- },
208
- [],
209
- )
210
-
211
- const first = extractDocumentTree(tree)
212
- const second = extractDocumentTree(tree)
213
- const third = extractDocumentTree(tree)
214
-
215
- expect(first.props.title).toBe('Export 1')
216
- expect(second.props.title).toBe('Export 2')
217
- expect(third.props.title).toBe('Export 3')
218
- })
219
-
220
- it('mixes function and plain values in the same _documentProps object', () => {
221
- const Image = docComponent('image')
222
- const tree = node(
223
- Image,
224
- {
225
- _documentProps: {
226
- src: 'static-url.png', // plain
227
- alt: () => 'dynamic alt text', // accessor
228
- width: 800, // plain number
229
- caption: () => 'caption ' + 42, // accessor returning a string
230
- },
231
- },
232
- [],
233
- )
234
-
235
- const result = extractDocumentTree(tree)
236
- expect(result.props).toEqual({
237
- src: 'static-url.png',
238
- alt: 'dynamic alt text',
239
- width: 800,
240
- caption: 'caption 42',
241
- })
242
- })
243
-
244
- it('preserves backward compatibility — existing primitives with plain props still work', () => {
245
- // Regression case: every existing document primitive uses
246
- // plain values in _documentProps. The function-resolution
247
- // change must not break them. This test mirrors the shape
248
- // of DocHeading's existing _documentProps.
249
- const Heading = docComponent('heading')
250
- const tree = node(
251
- Heading,
252
- { _documentProps: { level: 1 } },
253
- ['Hello'],
254
- )
255
-
256
- const result = extractDocumentTree(tree)
257
- expect(result.props).toEqual({ level: 1 })
258
- expect(typeof result.props.level).toBe('number')
259
- })
260
- })
261
-
262
- describe('component invocation path (extracts from post-attrs vnodes)', () => {
263
- // Real-world case: rocketstyle-based primitives never have
264
- // `_documentProps` on the JSX vnode. The user passes
265
- // `<DocDocument title="X" />`, which produces a JSX vnode
266
- // with `props = { title: 'X' }`. The `_documentProps` only
267
- // appears AFTER the rocketstyle attrs HOC runs the
268
- // `.attrs()` callback.
269
- //
270
- // Before the fix, extractDocumentTree only looked for
271
- // `_documentProps` on the JSX vnode's props directly — so
272
- // every real primitive's metadata was silently dropped during
273
- // export. The mock-vnode tests above hand-constructed
274
- // `_documentProps` to bypass this and never noticed.
275
- //
276
- // After the fix, extractDocumentTree CALLS the component
277
- // function for documentType vnodes that don't have
278
- // `_documentProps` directly, captures the post-attrs result,
279
- // and reads `_documentProps` from THAT.
280
- //
281
- // These tests use a hand-constructed component that mimics
282
- // the rocketstyle attrs pattern: the component is a function
283
- // with `_documentType` set, and calling it returns a vnode
284
- // whose props contain `_documentProps`. No real rocketstyle
285
- // dependency needed for the unit test.
286
-
287
- it('calls the component function and reads _documentProps from the post-attrs vnode', () => {
288
- // Component that mimics a rocketstyle-wrapped primitive:
289
- // takes user props, returns a vnode with _documentProps
290
- // populated by the "attrs callback".
291
- const DocDocLike = ((userProps: { title?: string; author?: string }) =>
292
- node('div', {
293
- _documentProps: {
294
- ...(userProps.title ? { title: userProps.title } : {}),
295
- ...(userProps.author ? { author: userProps.author } : {}),
296
- },
297
- })) as ((...args: any[]) => any) & DocumentMarker
298
- ;(DocDocLike as any)._documentType = 'document'
299
-
300
- // The JSX vnode has user props but NO _documentProps directly
301
- const jsxVnode = node(DocDocLike, { title: 'My Doc', author: 'Alice' }, [])
302
-
303
- const result = extractDocumentTree(jsxVnode)
304
-
305
- expect(result.type).toBe('document')
306
- expect(result.props.title).toBe('My Doc')
307
- expect(result.props.author).toBe('Alice')
308
- })
309
-
310
- it('also resolves function values from the post-attrs path', () => {
311
- // The two fixes compose: a real primitive that stores
312
- // accessor functions in its _documentProps (via the attrs
313
- // callback) gets the live value resolved at extraction time.
314
- let liveTitle = 'First'
315
- const DocDocLike = ((userProps: { title?: () => string }) =>
316
- node('div', {
317
- _documentProps: {
318
- title: userProps.title, // store the accessor as-is
319
- },
320
- })) as ((...args: any[]) => any) & DocumentMarker
321
- ;(DocDocLike as any)._documentType = 'document'
322
-
323
- const jsxVnode = node(DocDocLike, { title: () => liveTitle }, [])
324
-
325
- const first = extractDocumentTree(jsxVnode)
326
- expect(first.props.title).toBe('First')
327
-
328
- liveTitle = 'Second'
329
- const second = extractDocumentTree(jsxVnode)
330
- expect(second.props.title).toBe('Second')
331
- })
332
-
333
- it('prefers JSX-vnode _documentProps when both paths are available (back-compat)', () => {
334
- // If a vnode has _documentProps directly on its props (the
335
- // mock-vnode test pattern), extractDocumentTree should use
336
- // it WITHOUT calling the component. This preserves the
337
- // existing tests' fast path and avoids invoking components
338
- // unnecessarily.
339
- let componentCalled = false
340
- const DocDocLike = (() => {
341
- componentCalled = true
342
- return node('div', { _documentProps: { title: 'from-call' } })
343
- }) as ((...args: any[]) => any) & DocumentMarker
344
- ;(DocDocLike as any)._documentType = 'document'
345
-
346
- const jsxVnode = node(
347
- DocDocLike,
348
- { _documentProps: { title: 'from-jsx' } },
349
- [],
350
- )
351
-
352
- const result = extractDocumentTree(jsxVnode)
353
- expect(result.props.title).toBe('from-jsx')
354
- expect(componentCalled).toBe(false)
355
- })
356
- })
357
- })
358
-
359
- // ─── Real h() round-trip (parallel to the mock-vnode tests above) ────────
360
- //
361
- // This is the exact file PR #197 fixed — `extractDocumentTree` was
362
- // silently dropping metadata from real rocketstyle primitives because
363
- // the existing tests ONLY used the local `node(...)` helper that
364
- // hardcodes `{ type, props, children }` literals. The mock path
365
- // worked; the real pipeline (where `_documentProps` is only attached
366
- // AFTER the attrs HOC runs) didn't. The `audit_test_environment` tool
367
- // from PR #311 flagged this file HIGH (27 mock-helper call-sites, 0
368
- // real `h()` calls, no `@pyreon/core` import).
369
- //
370
- // This block adds a parallel: the same tree shapes built via real
371
- // `h(...)` from `@pyreon/core`. The mock `node()` helper returns a
372
- // hand-built object literal; real `h()` returns whatever the current
373
- // Pyreon VNode shape is — if the two ever drift, only the real-`h()`
374
- // path catches it. The mock tests above stay as the fast unit-test
375
- // path; these are the safety net.
376
-
377
- describe('extractDocumentTree — real h() round-trip', () => {
378
- it('extracts a simple document node built via real h()', () => {
379
- const Heading = docComponent('heading')
380
- const tree = h(
381
- Heading,
382
- { $rocketstyle: { fontSize: 24, fontWeight: 'bold' }, _documentProps: { level: 1 } },
383
- 'Hello World',
384
- )
385
-
386
- const result = extractDocumentTree(tree)
387
- expect(result.type).toBe('heading')
388
- expect(result.props).toEqual({ level: 1 })
389
- expect(result.children).toEqual(['Hello World'])
390
- expect(result.styles).toEqual({ fontSize: 24, fontWeight: 'bold' })
391
- })
392
-
393
- it('extracts nested document nodes through real h() trees', () => {
394
- const Section = docComponent('section')
395
- const Text = docComponent('text')
396
-
397
- const tree = h(
398
- Section,
399
- { $rocketstyle: { padding: 16 } },
400
- h(Text, { $rocketstyle: { fontSize: 14 } }, 'Paragraph one'),
401
- h(Text, { $rocketstyle: { fontSize: 14 } }, 'Paragraph two'),
402
- )
403
-
404
- const result = extractDocumentTree(tree)
405
- expect(result.type).toBe('section')
406
- expect(result.styles).toEqual({ padding: 16 })
407
- expect(result.children).toHaveLength(2)
408
- const child0 = result.children[0] as { type: string; children: unknown[] }
409
- const child1 = result.children[1] as { type: string; children: unknown[] }
410
- expect(child0.type).toBe('text')
411
- expect(child0.children).toEqual(['Paragraph one'])
412
- expect(child1.type).toBe('text')
413
- expect(child1.children).toEqual(['Paragraph two'])
414
- })
415
-
416
- it('transparent wrappers (no _documentType) are flattened by the extractor', () => {
417
- const Section = docComponent('section')
418
- const Text = docComponent('text')
419
- // A plain `div` wrapper nested inside a section — no document
420
- // marker on the div — should be invisible to the extractor.
421
- // Consumers sprinkle layout containers without breaking the
422
- // extraction pipeline.
423
- const tree = h(
424
- Section,
425
- {},
426
- h('div', {}, h(Text, { $rocketstyle: { fontSize: 14 } }, 'Hello')),
427
- )
428
-
429
- const result = extractDocumentTree(tree)
430
- expect(result.type).toBe('section')
431
- expect(result.children).toHaveLength(1)
432
- const child0 = result.children[0] as { type: string; children: unknown[] }
433
- expect(child0.type).toBe('text')
434
- expect(child0.children).toEqual(['Hello'])
435
- })
436
-
437
- it('component is INVOKED during extraction (the PR #197 fix)', () => {
438
- // The fix in PR #197: when a docComponent has attrs-HOC-style
439
- // post-processing, extractDocumentTree must call the component to
440
- // see its post-attrs VNode. With real `h()` the contract is the
441
- // same — the component function on `vnode.type` must be invoked
442
- // so that attrs-populated `_documentProps` surface correctly.
443
- let callCount = 0
444
- const Enriched = docComponent('heading', (props: any) => {
445
- callCount++
446
- // Mimic an attrs HOC that stamps post-attrs metadata onto a
447
- // child VNode instead of the outer wrapper (the exact shape
448
- // PR #197 discovered).
449
- return h('div', { ...props, _documentProps: { level: 2 } }, props.children)
450
- })
451
-
452
- const tree = h(Enriched, {}, 'After attrs')
453
- const result = extractDocumentTree(tree)
454
- expect(callCount).toBeGreaterThan(0)
455
- expect(result.type).toBe('heading')
456
- expect(result.props.level).toBe(2)
457
- })
458
- })
459
-
460
- // ─── T3.1 hoisted-attrs fast path (Path C) ────────────────────────────
461
- //
462
- // Real rocketstyle primitives now expose `__rs_attrs` — the accumulated
463
- // `.attrs()` callback chain — on the component function itself.
464
- // `extractDocumentTree` runs that chain DIRECTLY instead of invoking
465
- // the full component, so `_documentProps` resolution doesn't pay for
466
- // the styled wrapper / dimension resolution / JSX tree creation.
467
- //
468
- // The test below mimics rocketstyle's exposed surface (no real
469
- // `@pyreon/rocketstyle` import needed — the contract is just "if
470
- // `__rs_attrs` is present on the component function, use it"). A
471
- // counter-spied "render" function asserts the body is NEVER called when
472
- // the fast path is taken.
473
-
474
- describe('extractDocumentTree — T3.1 hoisted-attrs fast path', () => {
475
- it('uses __rs_attrs without invoking the component function (Path C)', () => {
476
- let callCount = 0
477
- // Mimic a rocketstyle primitive: function with _documentType static
478
- // AND __rs_attrs static (the hoisted attrs chain). The body is what
479
- // would be the styled wrapper; we count its invocations.
480
- const FakeRocketDoc = ((props: any) => {
481
- callCount++
482
- return h('div', props, props.children)
483
- }) as ((p: any) => any) & DocumentMarker
484
- ;(FakeRocketDoc as any)._documentType = 'document'
485
- ;(FakeRocketDoc as any).__rs_attrs = [
486
- (props: { title?: string; author?: string }) => ({
487
- _documentProps: {
488
- ...(props.title ? { title: props.title } : {}),
489
- ...(props.author ? { author: props.author } : {}),
490
- },
491
- }),
492
- ]
493
-
494
- const tree = h(FakeRocketDoc, { title: 'My Doc', author: 'Alice' })
495
- const result = extractDocumentTree(tree)
496
-
497
- expect(result.type).toBe('document')
498
- expect(result.props.title).toBe('My Doc')
499
- expect(result.props.author).toBe('Alice')
500
- // The architectural assertion — the component body must NOT run.
501
- expect(callCount).toBe(0)
502
- })
503
-
504
- it('also resolves accessor function values from __rs_attrs (composes with D1 fix)', () => {
505
- let liveTitle = 'First'
506
- let callCount = 0
507
- const FakeRocketDoc = ((props: any) => {
508
- callCount++
509
- return h('div', props, props.children)
510
- }) as ((p: any) => any) & DocumentMarker
511
- ;(FakeRocketDoc as any)._documentType = 'document'
512
- ;(FakeRocketDoc as any).__rs_attrs = [
513
- (props: { title?: () => string }) => ({
514
- _documentProps: { title: props.title },
515
- }),
516
- ]
517
-
518
- const jsx = h(FakeRocketDoc, { title: () => liveTitle })
519
- const first = extractDocumentTree(jsx)
520
- expect(first.props.title).toBe('First')
521
-
522
- liveTitle = 'Second'
523
- const second = extractDocumentTree(jsx)
524
- expect(second.props.title).toBe('Second')
525
-
526
- // Two extractions, zero component invocations.
527
- expect(callCount).toBe(0)
528
- })
529
-
530
- it('falls back to Path B (full component invocation) when __rs_attrs is absent', () => {
531
- // Non-rocketstyle docComponents (test fixtures, hand-rolled HOCs)
532
- // don't have __rs_attrs and must still work via the legacy Path B.
533
- let callCount = 0
534
- const PlainDoc = ((props: any) => {
535
- callCount++
536
- return h('div', { ...props, _documentProps: { level: 3 } }, props.children)
537
- }) as ((p: any) => any) & DocumentMarker
538
- ;(PlainDoc as any)._documentType = 'heading'
539
- // Note: NO __rs_attrs here — Path B should fire
540
-
541
- const tree = h(PlainDoc, {}, 'Body')
542
- const result = extractDocumentTree(tree)
543
-
544
- expect(result.type).toBe('heading')
545
- expect(result.props.level).toBe(3)
546
- expect(callCount).toBeGreaterThan(0)
547
- })
548
- })
@@ -1,124 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { resolveStyles } from '../resolveStyles'
3
-
4
- describe('resolveStyles', () => {
5
- it('resolves typography properties', () => {
6
- const result = resolveStyles({
7
- fontSize: '14px',
8
- fontFamily: 'system-ui, sans-serif',
9
- fontWeight: 'bold',
10
- fontStyle: 'italic',
11
- textDecoration: 'underline',
12
- color: '#333333',
13
- textAlign: 'center',
14
- lineHeight: 1.5,
15
- letterSpacing: '0.5px',
16
- })
17
-
18
- expect(result).toEqual({
19
- fontSize: 14,
20
- fontFamily: 'system-ui, sans-serif',
21
- fontWeight: 'bold',
22
- fontStyle: 'italic',
23
- textDecoration: 'underline',
24
- color: '#333333',
25
- textAlign: 'center',
26
- lineHeight: 1.5,
27
- letterSpacing: 0.5,
28
- })
29
- })
30
-
31
- it('resolves box model properties', () => {
32
- const result = resolveStyles({
33
- padding: '8px 16px',
34
- margin: '12px',
35
- })
36
-
37
- expect(result).toEqual({
38
- padding: [8, 16],
39
- margin: 12,
40
- })
41
- })
42
-
43
- it('resolves border properties', () => {
44
- const result = resolveStyles({
45
- borderRadius: '4px',
46
- borderWidth: '1px',
47
- borderColor: '#dddddd',
48
- borderStyle: 'solid',
49
- })
50
-
51
- expect(result).toEqual({
52
- borderRadius: 4,
53
- borderWidth: 1,
54
- borderColor: '#dddddd',
55
- borderStyle: 'solid',
56
- })
57
- })
58
-
59
- it('resolves sizing properties', () => {
60
- const result = resolveStyles({
61
- width: '200px',
62
- height: 100,
63
- maxWidth: '100%',
64
- })
65
-
66
- expect(result).toEqual({
67
- width: 200,
68
- height: 100,
69
- maxWidth: '100%',
70
- })
71
- })
72
-
73
- it('handles numeric values directly', () => {
74
- const result = resolveStyles({
75
- fontSize: 14,
76
- padding: 8,
77
- opacity: 0.5,
78
- })
79
-
80
- expect(result).toEqual({
81
- fontSize: 14,
82
- padding: 8,
83
- opacity: 0.5,
84
- })
85
- })
86
-
87
- it('ignores irrelevant CSS properties', () => {
88
- const result = resolveStyles({
89
- fontSize: 14,
90
- transition: 'all 0.2s',
91
- cursor: 'pointer',
92
- display: 'flex',
93
- position: 'relative',
94
- transform: 'translateX(10px)',
95
- })
96
-
97
- expect(result).toEqual({ fontSize: 14 })
98
- })
99
-
100
- it('skips invalid values', () => {
101
- const result = resolveStyles({
102
- fontStyle: 'oblique',
103
- textDecoration: 'overline',
104
- borderStyle: 'none',
105
- textAlign: 'start',
106
- })
107
-
108
- expect(result).toEqual({})
109
- })
110
-
111
- it('returns empty object for empty input', () => {
112
- expect(resolveStyles({})).toEqual({})
113
- })
114
-
115
- it('handles backgroundColor', () => {
116
- const result = resolveStyles({ backgroundColor: '#4f46e5' })
117
- expect(result).toEqual({ backgroundColor: '#4f46e5' })
118
- })
119
-
120
- it('converts rem values using rootSize', () => {
121
- const result = resolveStyles({ fontSize: '1.5rem' }, 20)
122
- expect(result).toEqual({ fontSize: 30 })
123
- })
124
- })