1
- const { getCSS, matcherTest, buildReturnMessage } = require ( ' ./utils' ) ;
1
+ const { getCSS, matcherTest, buildReturnMessage } = require ( " ./utils" ) ;
2
2
3
- const shouldDive = node => typeof node . dive === 'function' && typeof node . type ( ) !== 'string' ;
3
+ const shouldDive = node =>
4
+ typeof node . dive === "function" && typeof node . type ( ) !== "string" ;
4
5
5
- const isTagWithClassName = node => node . exists ( ) && node . prop ( 'className' ) && typeof node . type ( ) === 'string' ;
6
+ const isTagWithClassName = node =>
7
+ node . exists ( ) && node . prop ( "className" ) && typeof node . type ( ) === "string" ;
6
8
7
9
const getClassNames = received => {
8
10
let className ;
9
11
10
12
if ( received ) {
11
- if ( received . $$typeof === Symbol . for ( ' react.test.json' ) ) {
13
+ if ( received . $$typeof === Symbol . for ( " react.test.json" ) ) {
12
14
className = received . props . className || received . props . class ;
13
- } else if ( typeof received . exists === ' function' && received . exists ( ) ) {
15
+ } else if ( typeof received . exists === " function" && received . exists ( ) ) {
14
16
const tree = shouldDive ( received ) ? received . dive ( ) : received ;
15
17
const components = tree . findWhere ( isTagWithClassName ) ;
16
18
if ( components . length ) {
17
- className = components . first ( ) . prop ( ' className' ) ;
19
+ className = components . first ( ) . prop ( " className" ) ;
18
20
}
19
21
} else if ( global . Element && received instanceof global . Element ) {
20
- className = Array . from ( received . classList ) . join ( ' ' ) ;
22
+ className = Array . from ( received . classList ) . join ( " " ) ;
21
23
}
22
24
}
23
25
24
26
return className ? className . split ( / \s / ) : [ ] ;
25
27
} ;
26
28
27
- const hasAtRule = options => Object . keys ( options ) . some ( option => [ 'media' , 'supports' ] . includes ( option ) ) ;
29
+ const hasAtRule = options =>
30
+ Object . keys ( options ) . some ( option => [ "media" , "supports" ] . includes ( option ) ) ;
28
31
29
32
const getAtRules = ( ast , options ) => {
30
33
const mediaRegex = / ( \( [ a - z - ] + : ) \s ? ( [ a - z 0 - 9 . ] + \) ) / g;
31
34
32
35
return Object . keys ( options )
33
36
. map ( option =>
34
37
ast . stylesheet . rules
35
- . filter ( rule => rule . type === option && rule [ option ] === options [ option ] . replace ( mediaRegex , '$1$2' ) )
38
+ . filter (
39
+ rule =>
40
+ rule . type === option &&
41
+ rule [ option ] === options [ option ] . replace ( mediaRegex , "$1$2" )
42
+ )
36
43
. map ( rule => rule . rules )
37
44
. reduce ( ( acc , rules ) => acc . concat ( rules ) , [ ] )
38
45
)
39
46
. reduce ( ( acc , rules ) => acc . concat ( rules ) , [ ] ) ;
40
47
} ;
41
48
42
- const getModifiedClassName = ( className , modifier = '' ) => {
49
+ const getModifiedClassName = ( className , staticClassName , modifier = "" ) => {
43
50
const classNameSelector = `.${ className } ` ;
44
- let prefix = '' ;
51
+ let prefix = "" ;
45
52
46
53
modifier = modifier . trim ( ) ;
47
- if ( modifier . includes ( '&' ) ) {
48
- modifier = modifier . replace ( / & / g, classNameSelector ) ;
54
+ if ( modifier . includes ( "&" ) ) {
55
+ modifier = modifier
56
+ // & combined with other selectors and not a precedence boost should be replaced with the static className, but the first instance should be the dynamic className
57
+ . replace ( / ( & [ ^ & ] + ?) & / g, `$1.${ staticClassName } ` )
58
+ . replace ( / & / g, classNameSelector ) ;
49
59
} else {
50
60
prefix += classNameSelector ;
51
61
}
52
62
const first = modifier [ 0 ] ;
53
- if ( first !== ':' && first !== '[' ) {
54
- prefix += ' ' ;
63
+ if ( first !== ":" && first !== "[" ) {
64
+ prefix += " " ;
55
65
}
56
66
57
67
return `${ prefix } ${ modifier } ` . trim ( ) ;
58
68
} ;
59
69
60
- const hasClassNames = ( classNames , selectors , options ) =>
61
- classNames . some ( className => selectors . includes ( getModifiedClassName ( className , options . modifier ) ) ) ;
70
+ const hasClassNames = ( classNames , selectors , options ) => {
71
+ const staticClassNames = classNames . filter ( x => x . startsWith ( "sc-" ) ) ;
72
+
73
+ return classNames . some ( className =>
74
+ staticClassNames . some ( staticClassName =>
75
+ selectors . includes (
76
+ getModifiedClassName (
77
+ className ,
78
+ staticClassName ,
79
+ options . modifier
80
+ ) . replace ( / [ ' " ] / g, '"' )
81
+ )
82
+ )
83
+ ) ;
84
+ } ;
62
85
63
86
const getRules = ( ast , classNames , options ) => {
64
- const rules = hasAtRule ( options ) ? getAtRules ( ast , options ) : ast . stylesheet . rules ;
65
-
66
- return rules . filter ( rule => rule . type === 'rule' && hasClassNames ( classNames , rule . selectors , options ) ) ;
87
+ const rules = hasAtRule ( options )
88
+ ? getAtRules ( ast , options )
89
+ : ast . stylesheet . rules ;
90
+
91
+ return rules . filter (
92
+ rule =>
93
+ rule . type === "rule" && hasClassNames ( classNames , rule . selectors , options )
94
+ ) ;
67
95
} ;
68
96
69
97
const handleMissingRules = options => ( {
70
98
pass : false ,
71
99
message : ( ) =>
72
100
`No style rules found on passed Component${
73
- Object . keys ( options ) . length ? ` using options:\n${ JSON . stringify ( options ) } ` : ''
74
- } `,
101
+ Object . keys ( options ) . length
102
+ ? ` using options:\n${ JSON . stringify ( options ) } `
103
+ : ""
104
+ } `
75
105
} ) ;
76
106
77
107
const getDeclaration = ( rule , property ) =>
78
108
rule . declarations
79
- . filter ( declaration => declaration . type === 'declaration' && declaration . property === property )
109
+ . filter (
110
+ declaration =>
111
+ declaration . type === "declaration" && declaration . property === property
112
+ )
80
113
. pop ( ) ;
81
114
82
- const getDeclarations = ( rules , property ) => rules . map ( rule => getDeclaration ( rule , property ) ) . filter ( Boolean ) ;
115
+ const getDeclarations = ( rules , property ) =>
116
+ rules . map ( rule => getDeclaration ( rule , property ) ) . filter ( Boolean ) ;
83
117
84
118
const normalizeOptions = options =>
85
119
options . modifier
86
120
? Object . assign ( { } , options , {
87
- modifier : Array . isArray ( options . modifier ) ? options . modifier . join ( '' ) : options . modifier ,
121
+ modifier : Array . isArray ( options . modifier )
122
+ ? options . modifier . join ( "" )
123
+ : options . modifier
88
124
} )
89
125
: options ;
90
126
@@ -101,11 +137,14 @@ function toHaveStyleRule(component, property, expected, options = {}) {
101
137
const declarations = getDeclarations ( rules , property ) ;
102
138
const declaration = declarations . pop ( ) || { } ;
103
139
const received = declaration . value ;
104
- const pass = ! received && ! expected && this . isNot ? false : matcherTest ( received , expected ) ;
140
+ const pass =
141
+ ! received && ! expected && this . isNot
142
+ ? false
143
+ : matcherTest ( received , expected ) ;
105
144
106
145
return {
107
146
pass,
108
- message : buildReturnMessage ( this . utils , pass , property , received , expected ) ,
147
+ message : buildReturnMessage ( this . utils , pass , property , received , expected )
109
148
} ;
110
149
}
111
150
0 commit comments