@caweb/cli 1.4.1 → 1.4.3

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 (56) hide show
  1. package/README.md +38 -12
  2. package/bin/css-audit/.editorconfig +12 -0
  3. package/bin/css-audit/.github/workflows/build-report.yml +46 -0
  4. package/bin/css-audit/.github/workflows/merge-trunk-to-report.yml +17 -0
  5. package/bin/css-audit/.github/workflows/node.yaml +32 -0
  6. package/bin/css-audit/.nvmrc +1 -0
  7. package/bin/css-audit/README.md +131 -0
  8. package/bin/css-audit/css-audit.config.js +13 -0
  9. package/bin/css-audit/index.js +38 -0
  10. package/bin/css-audit/package-lock.json +6689 -0
  11. package/bin/css-audit/package.json +56 -0
  12. package/bin/css-audit/public/.gitkeep +1 -0
  13. package/bin/css-audit/src/__tests__/alphas.js +128 -0
  14. package/bin/css-audit/src/__tests__/colors.js +115 -0
  15. package/bin/css-audit/src/__tests__/display-none.js +52 -0
  16. package/bin/css-audit/src/__tests__/important.js +88 -0
  17. package/bin/css-audit/src/__tests__/media-queries.js +84 -0
  18. package/bin/css-audit/src/__tests__/property-values.js +55 -0
  19. package/bin/css-audit/src/__tests__/run.js +25 -0
  20. package/bin/css-audit/src/__tests__/selectors.js +66 -0
  21. package/bin/css-audit/src/audits/alphas.js +70 -0
  22. package/bin/css-audit/src/audits/colors.js +83 -0
  23. package/bin/css-audit/src/audits/display-none.js +39 -0
  24. package/bin/css-audit/src/audits/important.js +60 -0
  25. package/bin/css-audit/src/audits/media-queries.js +96 -0
  26. package/bin/css-audit/src/audits/property-values.js +65 -0
  27. package/bin/css-audit/src/audits/selectors.js +67 -0
  28. package/bin/css-audit/src/audits/typography.js +41 -0
  29. package/bin/css-audit/src/formats/cli-table.js +81 -0
  30. package/bin/css-audit/src/formats/html/_audit-alpha.twig +23 -0
  31. package/bin/css-audit/src/formats/html/_audit-colors.twig +23 -0
  32. package/bin/css-audit/src/formats/html/_audit-default.twig +24 -0
  33. package/bin/css-audit/src/formats/html/index.twig +88 -0
  34. package/bin/css-audit/src/formats/html/style.css +341 -0
  35. package/bin/css-audit/src/formats/html.js +52 -0
  36. package/bin/css-audit/src/formats/json.js +9 -0
  37. package/bin/css-audit/src/run.js +76 -0
  38. package/bin/css-audit/src/utils/__tests__/cli.js +70 -0
  39. package/bin/css-audit/src/utils/__tests__/example-config.config.js +12 -0
  40. package/bin/css-audit/src/utils/__tests__/get-specificity.js +39 -0
  41. package/bin/css-audit/src/utils/cli.js +133 -0
  42. package/bin/css-audit/src/utils/format-report.js +37 -0
  43. package/bin/css-audit/src/utils/get-specificity.js +97 -0
  44. package/bin/css-audit/src/utils/get-values-count.js +17 -0
  45. package/commands/audit.js +135 -0
  46. package/commands/index.js +7 -4
  47. package/commands/webpack/webpack.js +133 -0
  48. package/configs/css-audit.config.cjs +9 -0
  49. package/configs/webpack.config.js +126 -78
  50. package/docs/CREDITS.MD +5 -0
  51. package/docs/ROADMAP.MD +6 -5
  52. package/lib/cli.js +13 -4
  53. package/lib/helpers.js +3 -0
  54. package/package.json +15 -8
  55. package/commands/build.js +0 -80
  56. package/commands/serve.js +0 -94
@@ -0,0 +1,88 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <link rel="stylesheet" href="./style.css">
7
+ <title>{{title}}</title>
8
+ </head>
9
+ <body>
10
+ <header class="site__header">
11
+ <h1 id="top">{{title}}</h1>
12
+
13
+ <a href="https://github.com/WordPress/css-audit">Github</a>
14
+
15
+ <nav class="nav">
16
+ <ul>
17
+ {# Create audit nav if required #}
18
+ {% if reports|length > 1 %}
19
+ {% for report in reports %}
20
+ <li>
21
+ <a href="#{{report.audit}}">
22
+ <strong>{{report.name}}</strong> audit
23
+ </a>
24
+ </li>
25
+ {% endfor %}
26
+ {% endif %}
27
+ <li>
28
+ <button class="btn button__theme-toggle" onClick="cssAudit.toggleDarkMode()" aria-label="Dark Mode"></button>
29
+ </li>
30
+ </ul>
31
+ </nav>
32
+
33
+ </header>
34
+
35
+ {% for report in reports %}
36
+ {# Start audit section #}
37
+ <section class="audit audit--{{report.template}}" id="{{report.audit}}">
38
+
39
+ <header class="audit__header">
40
+ <h2 class="audit__title">
41
+ <strong>{{ report.name }}</strong> <em>audit</em>
42
+ </h2>
43
+ <a class="btn back-to-top" href="#top" title="Back to top">⬆</a>
44
+ </header>
45
+
46
+ {# Use specified template, or default template #}
47
+ {% if report.template %}
48
+ {% include '_audit-' ~ report.template ~ '.twig' with { data: report } %}
49
+ {% else %}
50
+ {% include '_audit-default.twig' with { data: report } %}
51
+ {% endif %}
52
+
53
+ </section>
54
+ {% endfor %}
55
+
56
+ {# Uncomment this to see the reports object - useful for debugging. #}
57
+ {#
58
+ <pre>
59
+ <code>{{dump(reports)}}</code>
60
+ </pre>
61
+ #}
62
+
63
+ <script>
64
+ const cssAudit = {};
65
+
66
+ document.addEventListener( 'DOMContentLoaded', () => {
67
+ const button = document.querySelector( '.button__theme-toggle' );
68
+
69
+ cssAudit.toggleDarkMode = function() {
70
+ const isDarkMode = document.body.classList.contains( 'is-dark-mode' );
71
+ if ( isDarkMode ) {
72
+ button.setAttribute( 'aria-pressed', 'false' );
73
+ document.body.classList.remove( 'is-dark-mode' );
74
+ } else {
75
+ button.setAttribute( 'aria-pressed', 'true' );
76
+ document.body.classList.add( 'is-dark-mode' );
77
+ }
78
+ }
79
+
80
+ // Set is-dark-mode class if user has requested dark mode.
81
+ if ( window.matchMedia( '(prefers-color-scheme: dark)' ).matches ){
82
+ document.body.classList.add( 'is-dark-mode' );
83
+ button.setAttribute( 'aria-pressed', 'true' );
84
+ }
85
+ } );
86
+ </script>
87
+ </body>
88
+ </html>
@@ -0,0 +1,341 @@
1
+ /*
2
+ * Config
3
+ */
4
+ body {
5
+ --body-margin: 2rem;
6
+
7
+ /* Body colours */
8
+ --body-bg: #fff;
9
+ --body-fg: #111;
10
+
11
+ /* Nav & link colours */
12
+ --nav-bg: #eee;
13
+ --nav-bg-hover: #bbb;
14
+ --nav-fg: #11c;
15
+
16
+ /* Heading colours */
17
+ --h-fg: #222;
18
+ --h-bg: #eee;
19
+
20
+ /* <hr> */
21
+ --hr: 4px solid var(--h-bg);
22
+
23
+ /* ul li::marker */
24
+ --ul-marker: #1114;
25
+ --ul-marker-hover: #111f;
26
+
27
+ /* Border style for bordered components */
28
+ --component-border: 1px solid #aaa;
29
+
30
+ /* Consistent component margins */
31
+ --component-margin-sm: 1.5rem;
32
+ --component-margin-lg: 3rem;
33
+
34
+ /* Code boxes */
35
+ --code-bg: #eee;
36
+ --code-border: var(--component-border);
37
+
38
+ /* Colour chips */
39
+ --chip-text-shadow: #fff;
40
+ --chip-border: var(--component-border);
41
+ --count-bg: #eee;
42
+ --count-border: var(--component-border);
43
+ }
44
+
45
+ /*
46
+ * Dark mode
47
+ */
48
+ body.is-dark-mode {
49
+ /* Body colours */
50
+ --body-bg: #111;
51
+ --body-fg: #fff;
52
+
53
+ /* Nav & link colours */
54
+ --nav-bg: #444;
55
+ --nav-bg-hover: #222;
56
+ --nav-fg: #bcf;
57
+
58
+ /* Heading colours */
59
+ --h-fg: #eee;
60
+ --h-bg: #222;
61
+
62
+ /* <hr> */
63
+ --hr: 4px solid var(--h-bg);
64
+
65
+ /* ul li::marker */
66
+ --ul-marker: #eee4;
67
+ --ul-marker-hover: #eeef;
68
+
69
+ /* Border style for bordered components */
70
+ --component-border: 1px solid #666;
71
+
72
+ /* Consistent component margins */
73
+ --component-margin-sm: 1.5rem;
74
+ --component-margin-lg: 3rem;
75
+
76
+ /* Code boxes */
77
+ --code-bg: #222;
78
+ --code-border: var(--component-border);
79
+
80
+ /* Colour chips */
81
+ --chip-text-shadow: #000;
82
+ --chip-border: var(--component-border);
83
+ --count-bg: #111;
84
+ --count-border: var(--component-border);
85
+ }
86
+
87
+ /*
88
+ * General
89
+ */
90
+ * {
91
+ box-sizing: border-box;
92
+ }
93
+
94
+ html {
95
+ scroll-behavior: smooth;
96
+
97
+ @media (prefers-reduced-motion: reduce) {
98
+ scroll-behavior: auto;
99
+ }
100
+ }
101
+
102
+ body {
103
+ background-color: var(--body-bg);
104
+ color: var(--body-fg);
105
+ font-family: sans-serif;
106
+ margin: var(--body-margin);
107
+ }
108
+
109
+ a {
110
+ color: var(--nav-fg);
111
+ }
112
+
113
+ h1, h2 {
114
+ font-weight: normal;
115
+ }
116
+
117
+ h1, h2, h3 {
118
+ color: var(--h-fg);
119
+ margin: 0 0 var(--component-margin-sm) 0;
120
+ }
121
+
122
+ hr {
123
+ border: 0;
124
+ border-bottom: var(--hr);
125
+ }
126
+
127
+ /*
128
+ * Header, nav, `back to top` button
129
+ */
130
+ .site__header {
131
+ align-items: baseline;
132
+ display: flex;
133
+ flex-wrap: wrap;
134
+ justify-content: space-between;
135
+ }
136
+
137
+ .nav {
138
+ margin-bottom: var(--component-margin-sm);
139
+ }
140
+
141
+ .nav ul {
142
+ display: flex;
143
+ justify-content: flex-start;
144
+ align-items: center;
145
+ list-style: none;
146
+ padding: 0;
147
+ }
148
+
149
+ .nav li {
150
+ margin-bottom: var(--component-margin-sm);
151
+ margin: 0 0.5em 0 0;
152
+ }
153
+
154
+ .nav a {
155
+ display: block;
156
+ height: 100%;
157
+ }
158
+
159
+ .nav a,
160
+ .btn {
161
+ background-color: var(--nav-bg);
162
+ padding: 0.5em;
163
+ text-decoration: none;
164
+ transition: background-color 0.2s ease;
165
+ }
166
+
167
+ .nav a:hover,
168
+ .nav a:focus,
169
+ .btn:hover,
170
+ .btn:focus {
171
+ background-color: var(--nav-bg-hover);
172
+ }
173
+
174
+ .nav a:focus,
175
+ .btn:focus {
176
+ outline: 4px dashed var(--nav-fg);
177
+ }
178
+
179
+ .btn {
180
+ font-size: 1.2em;
181
+ margin-bottom: var(--component-margin-sm);
182
+ text-align: center;
183
+ }
184
+
185
+ /*
186
+ * Audits
187
+ */
188
+ .audit__header {
189
+ align-items: baseline;
190
+ background-color: var(--h-bg);
191
+ display: flex;
192
+ justify-content: space-between;
193
+ margin-right: calc(-1 * var(--body-margin));
194
+ margin-left: calc(-1 * var(--body-margin));
195
+ margin-bottom: var(--component-margin-sm);
196
+ padding: var(--component-margin-sm) var(--body-margin) 0;
197
+ position: sticky;
198
+ top: 0;
199
+ z-index: 1;
200
+ }
201
+
202
+ .audit {
203
+ margin-bottom: var(--component-margin-lg);
204
+ }
205
+
206
+ /*
207
+ * Code boxes
208
+ */
209
+ code {
210
+ background-color: var(--code-bg);
211
+ border: var(--code-border);
212
+ border-radius: 1px;
213
+ display: inline-block;
214
+ font-family: monospace;
215
+ font-size: 1.2em;
216
+ padding: 0.5em;
217
+ }
218
+
219
+ /*
220
+ * Audit lists
221
+ */
222
+ .audit ul,
223
+ .audit ol {
224
+ padding: 0 0 0 2em;
225
+ }
226
+
227
+ .audit ol {
228
+ list-style-type: decimal-leading-zero;
229
+ }
230
+
231
+ .audit li {
232
+ margin: 0 0 2em 0;
233
+ }
234
+
235
+ .audit ol > li::marker {
236
+ color: var(--ul-marker);
237
+ }
238
+
239
+ .audit ol > li:hover::marker {
240
+ color: var(--ul-marker-hover);
241
+ }
242
+
243
+ /*
244
+ Audit: Colors
245
+ */
246
+ .audit--colors ul,
247
+ .audit--alpha ul {
248
+ display: flex;
249
+ flex-wrap: wrap;
250
+ font-family: monospace;
251
+ font-size: 1.2em;
252
+ list-style: none;
253
+ padding: 0;
254
+ }
255
+
256
+ .audit--colors .chip,
257
+ .audit--alpha .chip {
258
+ border: var(--chip-border);
259
+ height: 3em;
260
+ line-height: 3em;
261
+ margin: 0.5em;
262
+ text-align: center;
263
+ flex-basis: 18em;
264
+ flex-grow: 1;
265
+ background-color: var( --chip-bg-color );
266
+ font-family: monospace;
267
+ position: relative;
268
+ }
269
+
270
+ .audit--colors .chip.is-transparent:before,
271
+ .audit--alpha .chip.is-transparent:before {
272
+ content: '';
273
+ position: absolute;
274
+ top: 0;
275
+ left: 0;
276
+ right: 0;
277
+ bottom: 0;
278
+ z-index: -1;
279
+ background: repeating-conic-gradient(#f0f0f1 0% 25%, transparent 0% 50%) 50% / 20px 20px
280
+ }
281
+
282
+ /*
283
+ * Improve visibility of colour chip
284
+ * text on low contrast backgrounds
285
+ */
286
+ .audit--colors .chip:hover,
287
+ .audit--alpha .chip:hover {
288
+ font-weight: bold;
289
+ text-shadow: 0 0 1px var(--chip-text-shadow),
290
+ 0 0 3px var(--chip-text-shadow),
291
+ 0 0 5px var(--chip-text-shadow),
292
+ 0 0 7px var(--chip-text-shadow);
293
+ }
294
+
295
+ .audit--colors .count,
296
+ .audit--alpha .count {
297
+ left: -1em;
298
+ min-width: 3em;
299
+ padding: unset;
300
+ position: absolute;
301
+ top: -0.125em;
302
+ }
303
+
304
+ /*
305
+ * Count component on colour chips
306
+ */
307
+ .count {
308
+ background-color: var(--count-bg);
309
+ border: var(--count-border);
310
+ border-radius: 50%;
311
+ box-sizing: border-box;
312
+ color: var(--count-fg);
313
+ display: inline-block;
314
+ height: auto;
315
+ min-width: 2.3em;
316
+ padding: 0.5em;
317
+ text-align: center;
318
+
319
+ /* Counteract .chip:hover */
320
+ font-weight: normal;
321
+ text-shadow: none;
322
+ }
323
+
324
+ /*
325
+ * Darkmode switch
326
+ */
327
+ .button__theme-toggle {
328
+ background: transparent;
329
+ border: none;
330
+ cursor: pointer;
331
+ margin-bottom: 0;
332
+ }
333
+
334
+ .button__theme-toggle::before {
335
+ content: "🌛";
336
+ font-size: 1.5rem;
337
+ }
338
+
339
+ body.is-dark-mode .button__theme-toggle::before {
340
+ content: "🌞";
341
+ }
@@ -0,0 +1,52 @@
1
+ const fs = require( 'fs-extra' );
2
+ const path = require( 'path' );
3
+ const { TwingEnvironment, TwingLoaderFilesystem } = require( 'twing' );
4
+
5
+ /**
6
+ * Internal dependencies
7
+ */
8
+ const { getArg } = require( '../utils/cli' );
9
+
10
+ const templatePath = path.join( __dirname, './html' );
11
+
12
+ /**
13
+ * Get the template file, falling back to index.twig if a custom file is not found.
14
+ *
15
+ * @param {string} name Name of the current report.
16
+ * @return {string} File name.
17
+ */
18
+ function getTemplateFile( name ) {
19
+ if ( fs.existsSync( `${ templatePath }/${ name }.twig` ) ) {
20
+ return `${ name }.twig`;
21
+ }
22
+ return 'index.twig';
23
+ }
24
+
25
+ module.exports = function ( reports ) {
26
+ const loader = new TwingLoaderFilesystem( templatePath );
27
+ const twing = new TwingEnvironment( loader, { debug: true } );
28
+
29
+ const reportName = getArg( '--filename' );
30
+ const reportTemplate = getTemplateFile( reportName );
31
+ const reportDestDir = path.join( __dirname, '..', '..', 'public' );
32
+ const reportDest = path.join( reportDestDir, `${ reportName }.html` );
33
+ const context = {
34
+ title: `CSS Audit for ${ reportName }`,
35
+ reports,
36
+ };
37
+
38
+ // Copy CSS src to /public
39
+ const cssSrc = path.join( __dirname, 'html', 'style.css' );
40
+ const cssDest = path.join( reportDestDir, 'style.css' );
41
+ fs.copyFile( cssSrc, cssDest );
42
+
43
+ twing
44
+ .render( reportTemplate, context )
45
+ .then( ( output ) => {
46
+ console.log( `Generated template for ${ reportName }.` );
47
+ fs.writeFileSync( reportDest, output );
48
+ } )
49
+ .catch( ( e ) => {
50
+ console.error( e );
51
+ } );
52
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Convert the report data to a JSON string.
3
+ *
4
+ * @param {Array<Array<Object>>} reports The list of report data.
5
+ * @return {string} reports as a JSON string.
6
+ */
7
+ module.exports = function ( reports ) {
8
+ return JSON.stringify( reports.map( ( data ) => data ) );
9
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ const { formatReport } = require( './utils/format-report' );
5
+ const { getArg } = require( './utils/cli' );
6
+
7
+ const runAudits = ( cssFiles ) => {
8
+ const audits = [];
9
+ const runAll = getArg( '--all' );
10
+ const runRecommended = getArg( '--recommended' );
11
+
12
+ if ( runAll || runRecommended || getArg( '--colors' ) ) {
13
+ audits.push( require( './audits/colors' )( cssFiles ) );
14
+ }
15
+ if ( runAll || runRecommended || getArg( '--important' ) ) {
16
+ audits.push( require( './audits/important' )( cssFiles ) );
17
+ }
18
+ if ( runAll || getArg( '--display-none' ) ) {
19
+ audits.push( require( './audits/display-none' )( cssFiles ) );
20
+ }
21
+ if ( runAll || runRecommended || getArg( '--selectors' ) ) {
22
+ audits.push( require( './audits/selectors' )( cssFiles ) );
23
+ }
24
+ if ( runAll || runRecommended || getArg( '--media-queries' ) ) {
25
+ audits.push( require( './audits/media-queries' )( cssFiles ) );
26
+ }
27
+ if ( getArg( '--typography' ) ) {
28
+ audits.push( require( './audits/typography' )( cssFiles ) );
29
+ }
30
+ if ( getArg( '--alphas' ) ) {
31
+ audits.push( require( './audits/alphas' )( cssFiles ) );
32
+ }
33
+
34
+ const propertyValues = getArg( '--property-values' );
35
+ const isPropertyValuesArray =
36
+ Array.isArray( propertyValues ) && propertyValues.length;
37
+
38
+ // Multiple property value arguments are only supported in config.
39
+ if ( isPropertyValuesArray ) {
40
+ propertyValues.forEach( ( values ) => {
41
+ audits.push(
42
+ require( './audits/property-values' )(
43
+ cssFiles,
44
+ values.split( ',' )
45
+ )
46
+ );
47
+ } );
48
+ } else if ( ! isPropertyValuesArray ) {
49
+ // Single property-value audit handling for CLI
50
+ if ( !! propertyValues ) {
51
+ audits.push(
52
+ require( './audits/property-values' )(
53
+ cssFiles,
54
+ propertyValues.split( ',' )
55
+ )
56
+ );
57
+ }
58
+ }
59
+
60
+ const reports = audits.flat().filter( Boolean );
61
+
62
+ const format = getArg( '--format' );
63
+
64
+ if ( 'html' === format && ! getArg( '--filename' ) ) {
65
+ console.error(
66
+ 'Could not run audits. \nAn argument for filename must be provided for the HTML format.'
67
+ );
68
+ return;
69
+ }
70
+
71
+ return formatReport( reports, format );
72
+ };
73
+
74
+ module.exports = {
75
+ runAudits,
76
+ };
@@ -0,0 +1,70 @@
1
+ const { getArg, getArgsFromCLI, getConfig } = require( '../cli' );
2
+
3
+ describe( 'Run Audits from CLI', () => {
4
+ it( 'should get args from the CLI', () => {
5
+ process.argv = [
6
+ '',
7
+ '',
8
+ '--format=html',
9
+ '--property-values=padding,padding-top',
10
+ '--property-values=font-size,font-weight',
11
+ '--media-queries',
12
+ ];
13
+
14
+ expect( getArgsFromCLI() ).toEqual( [
15
+ '--format=html',
16
+ '--property-values=padding,padding-top',
17
+ '--property-values=font-size,font-weight',
18
+ '--media-queries',
19
+ ] );
20
+ } );
21
+
22
+ it( 'should return true for basic audit args in the CLI', () => {
23
+ process.argv = [ '', '', '--media-queries' ];
24
+
25
+ expect( getArg( '--media-queries' ) ).toBe( true );
26
+ } );
27
+
28
+ it( 'should return values for args that have them in the CLI', () => {
29
+ process.argv = [ '', '', '--property-values=padding,padding-top' ];
30
+
31
+ expect( getArg( '--property-values' ) ).toBe( 'padding,padding-top' );
32
+ } );
33
+ } );
34
+
35
+ describe( 'Run Audits from Config', () => {
36
+ beforeAll( () => {
37
+ process.argv = [ '', '', '' ];
38
+ } );
39
+
40
+ it( 'should return the value for config keys', () => {
41
+ expect( getArg( '--format' ) ).toBe( 'json' );
42
+ } );
43
+
44
+ it( 'should return true if the arg is a item in the config audits array', () => {
45
+ expect( getArg( '--important' ) ).toBe( true );
46
+ } );
47
+
48
+ it( 'should return an array of values for each property-value audits', () => {
49
+ expect( getArg( '--property-values' ) ).toStrictEqual( [
50
+ 'font-size',
51
+ 'padding-top,padding-bottom',
52
+ ] );
53
+ } );
54
+
55
+ it( 'should return false if arg is CLI only', () => {
56
+ expect( getArg( '--help', true ) ).toBe( false );
57
+ } );
58
+
59
+ it( 'should return false if an arg does not exist in CLI or config', () => {
60
+ process.argv = [ '', '', '--media-queries' ];
61
+
62
+ expect( getArg( '--nonexistant' ) ).toBe( false );
63
+ } );
64
+ } );
65
+
66
+ describe( 'Configuration', () => {
67
+ it( 'should get configuration from a file', () => {
68
+ expect( typeof getConfig() ).toBe( 'object' );
69
+ } );
70
+ } );
@@ -0,0 +1,12 @@
1
+ module.exports = {
2
+ format: 'json',
3
+ audits: [
4
+ 'colors',
5
+ 'important',
6
+ 'display-none',
7
+ 'selectors',
8
+ 'media-queries',
9
+ [ 'property-values', 'font-size' ],
10
+ [ 'property-values', 'padding-top,padding-bottom' ],
11
+ ],
12
+ };
@@ -0,0 +1,39 @@
1
+ const { getSpecificity } = require( '../get-specificity' );
2
+
3
+ describe( 'Calculate Specificity', () => {
4
+ it( 'should calculate for element selectors', () => {
5
+ expect( getSpecificity( 'body' ) ).toBe( 1 );
6
+ expect( getSpecificity( 'body *' ) ).toBe( 1 );
7
+ expect( getSpecificity( ':not(p)' ) ).toBe( 1 );
8
+ } );
9
+
10
+ it( 'should calculate for pseudo-elements', () => {
11
+ expect( getSpecificity( 'p::first-line ' ) ).toBe( 2 );
12
+ } );
13
+
14
+ it( 'should calculate for pseudo-classes', () => {
15
+ expect( getSpecificity( ':checked' ) ).toBe( 10 );
16
+ expect( getSpecificity( 'a:link' ) ).toBe( 11 );
17
+ } );
18
+
19
+ it( 'should calculate for class selectors', () => {
20
+ expect( getSpecificity( '.is-active' ) ).toBe( 10 );
21
+ } );
22
+
23
+ it( 'should calculate for attribute selectors', () => {
24
+ expect( getSpecificity( '[type="radio"]' ) ).toBe( 10 );
25
+ } );
26
+
27
+ it( 'should calculate for id selectors', () => {
28
+ expect( getSpecificity( '#unique' ) ).toBe( 100 );
29
+ } );
30
+
31
+ it( 'should calculate for combined selectors', () => {
32
+ expect( getSpecificity( 'main p:first-child::first-line' ) ).toBe( 13 );
33
+ expect( getSpecificity( '#unique > p' ) ).toBe( 101 );
34
+ expect( getSpecificity( '.home .container:not(nav)' ) ).toBe( 21 );
35
+ expect(
36
+ getSpecificity( 'li > a[href*="en-US"] > .inline-warning' )
37
+ ).toBe( 22 );
38
+ } );
39
+ } );