@pyreon/styler 0.24.5 → 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.
- package/package.json +5 -7
- package/src/ThemeProvider.ts +0 -65
- package/src/__tests__/ThemeProvider.test.ts +0 -67
- package/src/__tests__/benchmark.bench.ts +0 -200
- package/src/__tests__/composition-chain.test.ts +0 -537
- package/src/__tests__/css.test.ts +0 -70
- package/src/__tests__/dev-gate-treeshake.test.ts +0 -85
- package/src/__tests__/forward.test.ts +0 -282
- package/src/__tests__/globalStyle.test.ts +0 -72
- package/src/__tests__/hash.test.ts +0 -70
- package/src/__tests__/hybrid-injection.test.ts +0 -225
- package/src/__tests__/index.ts +0 -14
- package/src/__tests__/inject-rules.browser.test.ts +0 -40
- package/src/__tests__/insertion-effect.test.ts +0 -119
- package/src/__tests__/integration-dom.test.ts +0 -58
- package/src/__tests__/integration.test.ts +0 -179
- package/src/__tests__/keyframes.test.ts +0 -68
- package/src/__tests__/memory-growth.test.ts +0 -220
- package/src/__tests__/native-marker.test.ts +0 -9
- package/src/__tests__/p3-features.test.ts +0 -316
- package/src/__tests__/resolve-cache.test.ts +0 -94
- package/src/__tests__/resolve.test.ts +0 -308
- package/src/__tests__/shared.test.ts +0 -133
- package/src/__tests__/sheet-advanced.test.ts +0 -659
- package/src/__tests__/sheet-split-atrules.test.ts +0 -410
- package/src/__tests__/sheet.test.ts +0 -250
- package/src/__tests__/static-styler-resolve-cost.test.ts +0 -160
- package/src/__tests__/styled-reactive.test.ts +0 -74
- package/src/__tests__/styled-ssr.test.ts +0 -75
- package/src/__tests__/styled.test.ts +0 -511
- package/src/__tests__/styler.browser.test.tsx +0 -194
- package/src/__tests__/theme.test.ts +0 -33
- package/src/__tests__/useCSS.test.ts +0 -172
- package/src/css.ts +0 -13
- package/src/env.d.ts +0 -6
- package/src/forward.ts +0 -308
- package/src/globalStyle.ts +0 -53
- package/src/hash.ts +0 -28
- package/src/index.ts +0 -15
- package/src/keyframes.ts +0 -36
- package/src/manifest.ts +0 -332
- package/src/resolve.ts +0 -225
- package/src/shared.ts +0 -22
- package/src/sheet.ts +0 -635
- package/src/styled.tsx +0 -503
- package/src/tests/manifest-snapshot.test.ts +0 -51
- package/src/useCSS.ts +0 -20
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
import type { VNode } from '@pyreon/core'
|
|
2
|
-
import { h } from '@pyreon/core'
|
|
3
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
4
|
-
import { css } from '../css'
|
|
5
|
-
import { createSheet, StyleSheet } from '../sheet'
|
|
6
|
-
import { styled } from '../styled'
|
|
7
|
-
|
|
8
|
-
describe('P3 features', () => {
|
|
9
|
-
describe('shouldForwardProp', () => {
|
|
10
|
-
it('allows custom prop filtering', () => {
|
|
11
|
-
const Comp = styled('div', {
|
|
12
|
-
shouldForwardProp: (prop) => prop !== 'color',
|
|
13
|
-
})`
|
|
14
|
-
display: flex;
|
|
15
|
-
`
|
|
16
|
-
|
|
17
|
-
const vnode = Comp({ color: 'red', title: 'hello' }) as VNode
|
|
18
|
-
expect(vnode.props.color).toBeUndefined()
|
|
19
|
-
expect(vnode.props.title).toBe('hello')
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('custom filter receives all non-system props', () => {
|
|
23
|
-
const forwarded: string[] = []
|
|
24
|
-
const Comp = styled('div', {
|
|
25
|
-
shouldForwardProp: (prop) => {
|
|
26
|
-
forwarded.push(prop)
|
|
27
|
-
return true
|
|
28
|
-
},
|
|
29
|
-
})`
|
|
30
|
-
display: flex;
|
|
31
|
-
`
|
|
32
|
-
|
|
33
|
-
Comp({ 'data-x': '1', title: 'hi' })
|
|
34
|
-
expect(forwarded).toContain('data-x')
|
|
35
|
-
expect(forwarded).toContain('title')
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('works with dynamic interpolations', () => {
|
|
39
|
-
const Comp = styled('div', {
|
|
40
|
-
shouldForwardProp: (prop) => prop === 'title',
|
|
41
|
-
})`
|
|
42
|
-
color: ${(p: any) => p.$color};
|
|
43
|
-
`
|
|
44
|
-
|
|
45
|
-
const vnode = Comp({ $color: 'red', title: 'yes', custom: 'no' }) as VNode
|
|
46
|
-
expect(vnode.props.title).toBe('yes')
|
|
47
|
-
expect(vnode.props.custom).toBeUndefined()
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('does not affect component wrapping (components receive all props)', () => {
|
|
51
|
-
const Inner = (props: { class?: string; myProp?: string }) =>
|
|
52
|
-
h('div', { class: props.class, 'data-my': props.myProp })
|
|
53
|
-
|
|
54
|
-
// shouldForwardProp is only for HTML elements
|
|
55
|
-
const Comp = styled(Inner, {
|
|
56
|
-
shouldForwardProp: () => false,
|
|
57
|
-
})`
|
|
58
|
-
color: red;
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
const vnode = Comp({ myProp: 'hello' }) as VNode
|
|
62
|
-
// Components always receive all props (no filtering)
|
|
63
|
-
// The VNode wraps Inner and should pass myProp through
|
|
64
|
-
expect(vnode.props.myProp).toBe('hello')
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
describe('styled(StyledComponent) — extending', () => {
|
|
69
|
-
it('extends a styled component', () => {
|
|
70
|
-
const Base = styled('div')`
|
|
71
|
-
color: red;
|
|
72
|
-
`
|
|
73
|
-
const Extended = styled(Base)`
|
|
74
|
-
font-size: 20px;
|
|
75
|
-
`
|
|
76
|
-
|
|
77
|
-
const vnode = Extended({}) as VNode
|
|
78
|
-
// Extended wraps Base, so Base applies its own className
|
|
79
|
-
// and Extended passes its className to Base as a prop
|
|
80
|
-
expect(vnode.props.class).toContain('pyr-')
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('extended component receives className from outer', () => {
|
|
84
|
-
const Base = styled('div')`
|
|
85
|
-
color: red;
|
|
86
|
-
`
|
|
87
|
-
const Extended = styled(Base)`
|
|
88
|
-
font-size: 20px;
|
|
89
|
-
`
|
|
90
|
-
|
|
91
|
-
const vnode = Extended({ className: 'user-cls' }) as VNode
|
|
92
|
-
expect(vnode.props.class).toContain('user-cls')
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('multi-level extension works', () => {
|
|
96
|
-
const L1 = styled('div')`
|
|
97
|
-
display: flex;
|
|
98
|
-
`
|
|
99
|
-
const L2 = styled(L1)`
|
|
100
|
-
color: red;
|
|
101
|
-
`
|
|
102
|
-
const L3 = styled(L2)`
|
|
103
|
-
font-size: 14px;
|
|
104
|
-
`
|
|
105
|
-
|
|
106
|
-
const vnode = L3({}) as VNode
|
|
107
|
-
expect(vnode.props.class).toContain('pyr-')
|
|
108
|
-
// L3 wraps L2 which wraps L1 which wraps 'div'.
|
|
109
|
-
// The outermost VNode's type is the next component in the chain.
|
|
110
|
-
expect(typeof vnode.type).toBe('function')
|
|
111
|
-
})
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
describe('HMR cleanup API', () => {
|
|
115
|
-
beforeEach(() => {
|
|
116
|
-
document.querySelectorAll('style[data-pyreon-styler]').forEach((el) => {
|
|
117
|
-
el.remove()
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('clearCache removes all cached entries', () => {
|
|
122
|
-
const s = createSheet()
|
|
123
|
-
s.insert('color: red;')
|
|
124
|
-
s.insert('color: blue;')
|
|
125
|
-
expect(s.cacheSize).toBe(2)
|
|
126
|
-
|
|
127
|
-
s.clearCache()
|
|
128
|
-
expect(s.cacheSize).toBe(0)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('clearAll removes cache, SSR buffer, and DOM rules', () => {
|
|
132
|
-
const s = new StyleSheet()
|
|
133
|
-
s.insert('color: red;')
|
|
134
|
-
s.insert('color: blue;')
|
|
135
|
-
expect(s.cacheSize).toBe(2)
|
|
136
|
-
|
|
137
|
-
s.clearAll()
|
|
138
|
-
expect(s.cacheSize).toBe(0)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
it('after clearCache, same CSS gets re-inserted', () => {
|
|
142
|
-
const s = createSheet()
|
|
143
|
-
s.insert('color: red;')
|
|
144
|
-
expect(s.cacheSize).toBe(1)
|
|
145
|
-
|
|
146
|
-
s.clearCache()
|
|
147
|
-
expect(s.cacheSize).toBe(0)
|
|
148
|
-
|
|
149
|
-
// Re-insert — should work since cache was cleared
|
|
150
|
-
s.insert('color: red;')
|
|
151
|
-
expect(s.cacheSize).toBe(1)
|
|
152
|
-
})
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
describe('HMR cleanup API (SSR mode)', () => {
|
|
156
|
-
let originalDocument: typeof document
|
|
157
|
-
|
|
158
|
-
beforeEach(() => {
|
|
159
|
-
originalDocument = globalThis.document
|
|
160
|
-
// @ts-expect-error - intentionally deleting for SSR simulation
|
|
161
|
-
delete globalThis.document
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
afterEach(() => {
|
|
165
|
-
globalThis.document = originalDocument
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
it('clearAll in SSR mode clears buffer and cache', () => {
|
|
169
|
-
const s = createSheet()
|
|
170
|
-
s.insert('color: red;')
|
|
171
|
-
expect(s.getStyles()).toContain('color: red;')
|
|
172
|
-
expect(s.cacheSize).toBe(1)
|
|
173
|
-
|
|
174
|
-
s.clearAll()
|
|
175
|
-
expect(s.getStyles()).toBe('')
|
|
176
|
-
expect(s.cacheSize).toBe(0)
|
|
177
|
-
})
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
describe('CSS nesting (& selectors)', () => {
|
|
181
|
-
it('& selectors pass through to the CSS rule', () => {
|
|
182
|
-
// Native CSS nesting is supported by modern browsers
|
|
183
|
-
// The resolver passes CSS through without transformation
|
|
184
|
-
const Comp = styled('div')`
|
|
185
|
-
color: red;
|
|
186
|
-
&:hover {
|
|
187
|
-
color: blue;
|
|
188
|
-
}
|
|
189
|
-
`
|
|
190
|
-
const vnode = Comp({}) as VNode
|
|
191
|
-
expect(vnode.props.class).toMatch(/^pyr-/)
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
it('nested & with pseudo-elements', () => {
|
|
195
|
-
const Comp = styled('div')`
|
|
196
|
-
position: relative;
|
|
197
|
-
&::before {
|
|
198
|
-
content: '';
|
|
199
|
-
display: block;
|
|
200
|
-
}
|
|
201
|
-
&::after {
|
|
202
|
-
content: '';
|
|
203
|
-
display: block;
|
|
204
|
-
}
|
|
205
|
-
`
|
|
206
|
-
const vnode = Comp({}) as VNode
|
|
207
|
-
expect(vnode.props.class).toMatch(/^pyr-/)
|
|
208
|
-
})
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
describe('edge cases', () => {
|
|
212
|
-
it('empty template with dynamic interpolation returning nothing', () => {
|
|
213
|
-
const Comp = styled('div')`
|
|
214
|
-
${(p: any) =>
|
|
215
|
-
p.$show &&
|
|
216
|
-
css`
|
|
217
|
-
color: red;
|
|
218
|
-
`}
|
|
219
|
-
`
|
|
220
|
-
const vnode = Comp({ $show: false }) as VNode
|
|
221
|
-
// When resolved CSS is empty/whitespace, no className
|
|
222
|
-
expect(vnode.props.class).toBeFalsy()
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
it('empty template with dynamic interpolation returning value', () => {
|
|
226
|
-
const Comp = styled('div')`
|
|
227
|
-
${(p: any) =>
|
|
228
|
-
p.$show &&
|
|
229
|
-
css`
|
|
230
|
-
color: red;
|
|
231
|
-
`}
|
|
232
|
-
`
|
|
233
|
-
const vnode = Comp({ $show: true }) as VNode
|
|
234
|
-
expect(vnode.props.class).toMatch(/^pyr-/)
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
it('deeply nested CSSResult chains resolve correctly', () => {
|
|
238
|
-
const l1 = css`
|
|
239
|
-
color: red;
|
|
240
|
-
`
|
|
241
|
-
const l2 = css`
|
|
242
|
-
${l1} font-size: 14px;
|
|
243
|
-
`
|
|
244
|
-
const l3 = css`
|
|
245
|
-
${l2} display: flex;
|
|
246
|
-
`
|
|
247
|
-
const l4 = css`
|
|
248
|
-
${l3} padding: 8px;
|
|
249
|
-
`
|
|
250
|
-
const l5 = css`
|
|
251
|
-
${l4} margin: 4px;
|
|
252
|
-
`
|
|
253
|
-
|
|
254
|
-
const resolved = l5.toString()
|
|
255
|
-
expect(resolved).toContain('color: red;')
|
|
256
|
-
expect(resolved).toContain('font-size: 14px;')
|
|
257
|
-
expect(resolved).toContain('display: flex;')
|
|
258
|
-
expect(resolved).toContain('padding: 8px;')
|
|
259
|
-
expect(resolved).toContain('margin: 4px;')
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
it('anonymous component gets fallback displayName', () => {
|
|
263
|
-
const Anon = (() => {
|
|
264
|
-
const fn = () => null
|
|
265
|
-
Object.defineProperty(fn, 'name', { value: '' })
|
|
266
|
-
return fn
|
|
267
|
-
})()
|
|
268
|
-
|
|
269
|
-
const Comp = styled(Anon)`
|
|
270
|
-
color: red;
|
|
271
|
-
`
|
|
272
|
-
expect((Comp as any).displayName).toBe('styled(Component)')
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
it('handles very large CSS strings', () => {
|
|
276
|
-
const bigCSS = Array.from({ length: 100 }, (_, i) => `prop${i}: val${i};`).join(' ')
|
|
277
|
-
const Comp = styled('div')`
|
|
278
|
-
${bigCSS}
|
|
279
|
-
`
|
|
280
|
-
const vnode = Comp({}) as VNode
|
|
281
|
-
expect(vnode.props.class).toMatch(/^pyr-/)
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
it('different dynamic values cause different classNames', () => {
|
|
285
|
-
const Comp = styled('div')`
|
|
286
|
-
color: ${(p: any) => p.$color};
|
|
287
|
-
font-size: ${(p: any) => p.$size};
|
|
288
|
-
`
|
|
289
|
-
|
|
290
|
-
const vnode1 = Comp({ $color: 'red', $size: '14px' }) as VNode
|
|
291
|
-
const cls1 = vnode1.props.class as string
|
|
292
|
-
|
|
293
|
-
const vnode2 = Comp({ $color: 'blue', $size: '16px' }) as VNode
|
|
294
|
-
const cls2 = vnode2.props.class as string
|
|
295
|
-
|
|
296
|
-
expect(cls1).not.toBe(cls2)
|
|
297
|
-
expect(cls1).toMatch(/^pyr-/)
|
|
298
|
-
expect(cls2).toMatch(/^pyr-/)
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
it('same dynamic values produce same className (dedup cache)', () => {
|
|
302
|
-
const Comp = styled('div')`
|
|
303
|
-
color: ${(p: any) => p.$color};
|
|
304
|
-
`
|
|
305
|
-
|
|
306
|
-
const vnode1 = Comp({ $color: 'red' }) as VNode
|
|
307
|
-
const cls1 = vnode1.props.class as string
|
|
308
|
-
|
|
309
|
-
// Re-render with same value
|
|
310
|
-
const vnode2 = Comp({ $color: 'red' }) as VNode
|
|
311
|
-
const cls2 = vnode2.props.class as string
|
|
312
|
-
|
|
313
|
-
expect(cls1).toBe(cls2)
|
|
314
|
-
})
|
|
315
|
-
})
|
|
316
|
-
})
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
-
import { normalizeCSS, resolve } from '../resolve'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Tier 2 cache correctness proof: demonstrates that the WeakMap cache
|
|
6
|
-
* in DynamicStyled correctly handles:
|
|
7
|
-
* 1. Same $rocketstyle + $rocketstate → cache hit (skip resolve)
|
|
8
|
-
* 2. Same $rocketstyle, different $rocketstate → cache miss (different CSS)
|
|
9
|
-
* 3. Different $rocketstyle → cache miss (different CSS)
|
|
10
|
-
*
|
|
11
|
-
* These tests verify the LOGIC, not the styled() integration (which
|
|
12
|
-
* runs in a browser environment). They prove the cache key scheme is
|
|
13
|
-
* correct: both $rocketstyle AND $rocketstate identity matter.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
describe('Tier 2: resolve cache correctness', () => {
|
|
17
|
-
const strings = Object.assign(['background-color: ', ';'] as unknown as TemplateStringsArray, { raw: ['background-color: ', ';'] })
|
|
18
|
-
const values = [(props: any) => props.$rocketstyle?.backgroundColor ?? 'transparent']
|
|
19
|
-
const theme = { colors: { primary: '#3b82f6' } }
|
|
20
|
-
|
|
21
|
-
it('same $rocketstyle object produces same CSS text (cache would hit)', () => {
|
|
22
|
-
const rs = { backgroundColor: 'red' }
|
|
23
|
-
const props1 = { $rocketstyle: rs, $rocketstate: { state: 'default' }, theme }
|
|
24
|
-
const props2 = { $rocketstyle: rs, $rocketstate: { state: 'default' }, theme }
|
|
25
|
-
|
|
26
|
-
const css1 = normalizeCSS(resolve(strings, values, props1))
|
|
27
|
-
const css2 = normalizeCSS(resolve(strings, values, props2))
|
|
28
|
-
|
|
29
|
-
expect(css1).toBe(css2)
|
|
30
|
-
expect(css1).toContain('red')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('different $rocketstyle objects produce different CSS (cache miss)', () => {
|
|
34
|
-
const rs1 = { backgroundColor: 'red' }
|
|
35
|
-
const rs2 = { backgroundColor: 'blue' }
|
|
36
|
-
|
|
37
|
-
const css1 = normalizeCSS(resolve(strings, values, { $rocketstyle: rs1, theme }))
|
|
38
|
-
const css2 = normalizeCSS(resolve(strings, values, { $rocketstyle: rs2, theme }))
|
|
39
|
-
|
|
40
|
-
expect(css1).not.toBe(css2)
|
|
41
|
-
expect(css1).toContain('red')
|
|
42
|
-
expect(css2).toContain('blue')
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('same $rocketstyle but different $rocketstate produces different CSS when state-dependent', () => {
|
|
46
|
-
// Interpolation that reads $rocketstate
|
|
47
|
-
const stateStrings = Object.assign(['opacity: ', ';'] as unknown as TemplateStringsArray, { raw: ['opacity: ', ';'] })
|
|
48
|
-
const stateValues = [(props: any) => props.$rocketstate?.disabled ? '0.5' : '1']
|
|
49
|
-
|
|
50
|
-
const rs = { backgroundColor: 'red' }
|
|
51
|
-
const state1 = { disabled: false }
|
|
52
|
-
const state2 = { disabled: true }
|
|
53
|
-
|
|
54
|
-
const css1 = normalizeCSS(resolve(stateStrings, stateValues, { $rocketstyle: rs, $rocketstate: state1, theme }))
|
|
55
|
-
const css2 = normalizeCSS(resolve(stateStrings, stateValues, { $rocketstyle: rs, $rocketstate: state2, theme }))
|
|
56
|
-
|
|
57
|
-
expect(css1).toContain('opacity: 1;')
|
|
58
|
-
expect(css2).toContain('opacity: 0.5;')
|
|
59
|
-
// Proves: caching on $rocketstyle alone would be WRONG here.
|
|
60
|
-
// The two-level WeakMap ($rocketstyle → $rocketstate → class) is correct.
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
describe('Tier 2: allocation reduction', () => {
|
|
65
|
-
it('resolve() is expensive — cache eliminates it for repeated identical inputs', () => {
|
|
66
|
-
const rs = { backgroundColor: 'red', color: 'white', padding: '8px' }
|
|
67
|
-
const strings = Object.assign(
|
|
68
|
-
['background-color: ', '; color: ', '; padding: ', ';'] as unknown as TemplateStringsArray,
|
|
69
|
-
{ raw: ['background-color: ', '; color: ', '; padding: ', ';'] },
|
|
70
|
-
)
|
|
71
|
-
const values = [
|
|
72
|
-
(p: any) => p.$rocketstyle?.backgroundColor,
|
|
73
|
-
(p: any) => p.$rocketstyle?.color,
|
|
74
|
-
(p: any) => p.$rocketstyle?.padding,
|
|
75
|
-
]
|
|
76
|
-
|
|
77
|
-
const resolveSpy = vi.fn(resolve)
|
|
78
|
-
|
|
79
|
-
// Simulate 50 renders with identical $rocketstyle
|
|
80
|
-
const results: string[] = []
|
|
81
|
-
for (let i = 0; i < 50; i++) {
|
|
82
|
-
const cssText = normalizeCSS(resolveSpy(strings, values, { $rocketstyle: rs, theme: {} }))
|
|
83
|
-
results.push(cssText)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// All 50 produce identical CSS
|
|
87
|
-
expect(new Set(results).size).toBe(1)
|
|
88
|
-
// resolve was called 50 times (no cache at this level — the cache is
|
|
89
|
-
// in DynamicStyled). This test just proves the CONTRACT: identical
|
|
90
|
-
// inputs → identical outputs, so caching is safe.
|
|
91
|
-
expect(resolveSpy).toHaveBeenCalledTimes(50)
|
|
92
|
-
expect(results[0]).toContain('background-color: red')
|
|
93
|
-
})
|
|
94
|
-
})
|