UNPKG

16.4 kBJavaScriptView Raw
1import asap from 'asap';
2import { assert } from 'chai';
3import { JSDOM } from 'jsdom';
4
5import {
6 StyleSheet,
7 StyleSheetServer,
8 StyleSheetTestUtils,
9 minify,
10 css
11} from '../src/index.js';
12import { reset } from '../src/inject.js';
13import { getSheetText } from './testUtils.js';
14
15describe('css', () => {
16 beforeEach(() => {
17 global.document = new JSDOM('').window.document;
18 reset();
19 });
20
21 afterEach(() => {
22 global.document.close();
23 global.document = undefined;
24 });
25
26 it('generates class names', () => {
27 const sheet = StyleSheet.create({
28 red: {
29 color: 'red',
30 },
31
32 blue: {
33 color: 'blue'
34 }
35 });
36
37 assert.ok(css(sheet.red, sheet.blue));
38 });
39
40 it('filters out falsy inputs', () => {
41 const sheet = StyleSheet.create({
42 red: {
43 color: 'red',
44 },
45 });
46
47 assert.equal(css(sheet.red), css(sheet.red, false));
48 assert.equal(css(sheet.red), css(false, sheet.red));
49 });
50
51 it('accepts arrays of styles', () => {
52 const sheet = StyleSheet.create({
53 red: {
54 color: 'red',
55 },
56
57 blue: {
58 color: 'blue'
59 }
60 });
61
62 assert.equal(css(sheet.red, sheet.blue), css([sheet.red, sheet.blue]));
63 assert.equal(css(sheet.red, sheet.blue), css(sheet.red, [sheet.blue]));
64 assert.equal(css(sheet.red, sheet.blue), css([sheet.red, [sheet.blue]]));
65 assert.equal(css(sheet.red), css(false, [null, false, sheet.red]));
66 });
67
68 it('succeeds for with empty args', () => {
69 assert(css() != null);
70 assert(css(false) != null);
71 });
72
73 it('adds styles to the DOM', done => {
74 const sheet = StyleSheet.create({
75 red: {
76 color: 'red',
77 },
78 });
79
80 css(sheet.red);
81
82 asap(() => {
83 const styleTags = global.document.getElementsByTagName("style");
84 const lastTag = styleTags[styleTags.length - 1];
85 const style = getSheetText(lastTag.sheet);
86
87 assert.include(style, `${sheet.red._name} {`);
88 assert.match(style, /color: red !important/);
89 done();
90 });
91 });
92
93 it('only ever creates one style tag', done => {
94 const sheet = StyleSheet.create({
95 red: {
96 color: 'red',
97 },
98 blue: {
99 color: 'blue',
100 },
101 });
102
103 css(sheet.red);
104
105 asap(() => {
106 const styleTags = global.document.getElementsByTagName("style");
107 assert.equal(styleTags.length, 1);
108
109 css(sheet.blue);
110
111 asap(() => {
112 const styleTags = global.document.getElementsByTagName("style");
113 assert.equal(styleTags.length, 1);
114 done();
115 });
116 });
117 });
118
119 it('automatically uses a style tag with the data-aphrodite attribute', done => {
120 const style = document.createElement("style");
121 style.setAttribute("data-aphrodite", "");
122 document.head.appendChild(style);
123
124 const sheet = StyleSheet.create({
125 red: {
126 color: 'red',
127 },
128 blue: {
129 color: 'blue',
130 },
131 });
132
133 css(sheet.red);
134
135 asap(() => {
136 const styleTags = global.document.getElementsByTagName("style");
137 assert.equal(styleTags.length, 1);
138 const styles = getSheetText(styleTags[0].sheet);
139
140 assert.include(styles, `${sheet.red._name} {`);
141 assert.include(styles, 'color: red');
142
143 done();
144 });
145 });
146
147 it("throws a useful error for invalid arguments", () => {
148 assert.throws(() => css({ color: "red" }), "Invalid Style Definition");
149 });
150});
151
152describe('StyleSheet.create', () => {
153 it('assigns a name to stylesheet properties', () => {
154 const sheet = StyleSheet.create({
155 red: {
156 color: 'red',
157 },
158 blue: {
159 color: 'blue'
160 }
161 });
162
163 assert.ok(sheet.red._name);
164 assert.ok(sheet.blue._name);
165 assert.notEqual(sheet.red._name, sheet.blue._name);
166 });
167
168 it('assign different names to two different create calls', () => {
169 const sheet1 = StyleSheet.create({
170 red: {
171 color: 'blue',
172 },
173 });
174
175 const sheet2 = StyleSheet.create({
176 red: {
177 color: 'red',
178 },
179 });
180
181 assert.notEqual(sheet1.red._name, sheet2.red._name);
182 });
183
184 it('assigns the same name to identical styles from different create calls', () => {
185 const sheet1 = StyleSheet.create({
186 red: {
187 color: 'red',
188 height: 20,
189
190 ':hover': {
191 color: 'blue',
192 width: 40,
193 },
194 },
195 });
196
197 const sheet2 = StyleSheet.create({
198 red: {
199 color: 'red',
200 height: 20,
201
202 ':hover': {
203 color: 'blue',
204 width: 40,
205 },
206 },
207 });
208
209 assert.equal(sheet1.red._name, sheet2.red._name);
210 });
211
212 it('hashes style names correctly', () => {
213 const sheet = StyleSheet.create({
214 test: {
215 color: 'red',
216 height: 20,
217
218 ':hover': {
219 color: 'blue',
220 width: 40,
221 },
222 },
223 });
224
225 assert.equal(sheet.test._name, 'test_j5rvvh');
226 });
227
228 it('works for empty stylesheets and styles', () => {
229 const emptySheet = StyleSheet.create({});
230
231 assert.ok(emptySheet);
232
233 const sheet = StyleSheet.create({
234 empty: {}
235 });
236
237 assert.ok(sheet.empty._name);
238 });
239});
240
241describe('minify', () => {
242 describe('true', () => {
243 beforeEach(() => {
244 minify(true);
245 });
246
247 afterEach(() => {
248 minify(undefined);
249 });
250
251 it('minifies style names', () => {
252 const sheet = StyleSheet.create({
253 test: {
254 color: 'red',
255 height: 20,
256
257 ':hover': {
258 color: 'blue',
259 width: 40,
260 },
261 },
262 });
263
264 assert.equal(sheet.test._name, 'j5rvvh');
265 });
266 })
267
268 describe('false', () => {
269 beforeEach(() => {
270 minify(false);
271 });
272
273 afterEach(() => {
274 minify(undefined);
275 });
276
277 it('does not minifies style names', () => {
278 const sheet = StyleSheet.create({
279 test: {
280 color: 'red',
281 height: 20,
282
283 ':hover': {
284 color: 'blue',
285 width: 40,
286 },
287 },
288 });
289
290 assert.equal(sheet.test._name, 'test_j5rvvh');
291 });
292
293 it('does not minifies style names, even with process.env.NODE_ENV === \'production\'', () => {
294 const sheet = StyleSheet.create({
295 test: {
296 color: 'red',
297 height: 20,
298
299 ':hover': {
300 color: 'blue',
301 width: 40,
302 },
303 },
304 });
305
306 assert.equal(sheet.test._name, 'test_j5rvvh');
307 });
308 })
309});
310
311describe('rehydrate', () => {
312 beforeEach(() => {
313 global.document = new JSDOM('').window.document;
314 reset();
315 });
316
317 afterEach(() => {
318 global.document.close();
319 global.document = undefined;
320 });
321
322 const sheet = StyleSheet.create({
323 red: {
324 color: 'red',
325 },
326
327 blue: {
328 color: 'blue',
329 },
330
331 green: {
332 color: 'green',
333 },
334 });
335
336 it('doesn\'t render styles in the renderedClassNames arg', done => {
337 StyleSheet.rehydrate([sheet.red._name, sheet.blue._name]);
338
339 css(sheet.red);
340 css(sheet.blue);
341 css(sheet.green);
342
343 asap(() => {
344 const styleTags = global.document.getElementsByTagName("style");
345 assert.equal(styleTags.length, 1);
346 const styles = getSheetText(styleTags[0].sheet);
347
348 assert.notInclude(styles, `.${sheet.red._name} {`);
349 assert.notInclude(styles, `.${sheet.blue._name} {`);
350 assert.include(styles, `.${sheet.green._name} {`);
351 assert.notMatch(styles, /color: blue/);
352 assert.notMatch(styles, /color: red/);
353 assert.match(styles, /color: green/);
354
355 done();
356 });
357 });
358
359 it('doesn\'t fail with no argument passed in', () => {
360 StyleSheet.rehydrate();
361 });
362});
363
364describe('StyleSheet.extend', () => {
365 beforeEach(() => {
366 global.document = new JSDOM('').window.document;
367 reset();
368 });
369
370 afterEach(() => {
371 global.document.close();
372 global.document = undefined;
373 });
374
375 it('accepts empty extensions', () => {
376 const newAphrodite = StyleSheet.extend([]);
377
378 assert(newAphrodite.css);
379 assert(newAphrodite.StyleSheet);
380 });
381
382 it('uses a new selector handler', done => {
383 const descendantHandler = (selector, baseSelector,
384 generateSubtreeStyles) => {
385 if (selector[0] !== '^') {
386 return null;
387 }
388 return generateSubtreeStyles(
389 `.${selector.slice(1)} ${baseSelector}`);
390 };
391
392 const descendantHandlerExtension = {
393 selectorHandler: descendantHandler,
394 };
395
396 // Pull out the new StyleSheet/css functions to use for the rest of
397 // this test.
398 const {StyleSheet: newStyleSheet, css: newCss} = StyleSheet.extend([
399 descendantHandlerExtension]);
400
401 const sheet = newStyleSheet.create({
402 foo: {
403 '^bar': {
404 '^baz': {
405 color: 'orange',
406 },
407 color: 'red',
408 },
409 color: 'blue',
410 },
411 });
412
413 newCss(sheet.foo);
414
415 asap(() => {
416 const styleTags = global.document.getElementsByTagName("style");
417 assert.equal(styleTags.length, 1);
418 const styles = getSheetText(styleTags[0].sheet);
419
420 assert.notInclude(styles, '^bar');
421 assert.include(styles, '.bar .foo');
422 assert.include(styles, '.baz .bar .foo');
423 assert.include(styles, 'color: red');
424 assert.include(styles, 'color: blue');
425 assert.include(styles, 'color: orange');
426
427 done();
428 });
429 });
430});
431
432describe('StyleSheetServer.renderStatic', () => {
433 const sheet = StyleSheet.create({
434 red: {
435 color: 'red',
436 },
437
438 blue: {
439 color: 'blue',
440 },
441
442 green: {
443 color: 'green',
444 },
445 });
446
447 it('returns the correct data', () => {
448 const render = () => {
449 css(sheet.red);
450 css(sheet.blue);
451
452 return "html!";
453 };
454
455 const ret = StyleSheetServer.renderStatic(render);
456
457 assert.equal(ret.html, "html!");
458
459 assert.include(ret.css.content, `.${sheet.red._name}{`);
460 assert.include(ret.css.content, `.${sheet.blue._name}{`);
461 assert.match(ret.css.content, /color:red/);
462 assert.match(ret.css.content, /color:blue/);
463
464 assert.include(ret.css.renderedClassNames, sheet.red._name);
465 assert.include(ret.css.renderedClassNames, sheet.blue._name);
466 });
467
468 it('succeeds even if a previous renderStatic crashed', () => {
469 const badRender = () => {
470 css(sheet.red);
471 css(sheet.blue);
472 throw new Error("boo!");
473 };
474
475 const goodRender = () => {
476 css(sheet.blue);
477 return "html!";
478 };
479
480 assert.throws(() => {
481 StyleSheetServer.renderStatic(badRender);
482 }, "boo!");
483
484 const ret = StyleSheetServer.renderStatic(goodRender);
485
486 assert.equal(ret.html, "html!");
487
488 assert.include(ret.css.content, `.${sheet.blue._name}{`);
489 assert.notInclude(ret.css.content, `.${sheet.red._name}{`);
490 assert.include(ret.css.content, 'color:blue');
491 assert.notInclude(ret.css.content, 'color:red');
492
493 assert.include(ret.css.renderedClassNames, sheet.blue._name);
494 assert.notInclude(ret.css.renderedClassNames, sheet.red._name);
495 });
496
497 it('doesn\'t mistakenly return styles if called a second time', () => {
498 const render = () => {
499 css(sheet.red);
500 css(sheet.blue);
501
502 return "html!";
503 };
504
505 const emptyRender = () => {
506 return "";
507 };
508
509 const ret = StyleSheetServer.renderStatic(render);
510 assert.notEqual(ret.css.content, "");
511
512 const newRet = StyleSheetServer.renderStatic(emptyRender);
513 assert.equal(newRet.css.content, "");
514 });
515
516 it('should inject unique font-faces by src', () => {
517 const fontSheet = StyleSheet.create({
518 test: {
519 fontFamily: [{
520 fontStyle: "normal",
521 fontWeight: "normal",
522 fontFamily: "My Font",
523 src: 'url(blah) format("woff"), url(blah) format("truetype")'
524 }, {
525 fontStyle: "italic",
526 fontWeight: "normal",
527 fontFamily: "My Font",
528 src: 'url(blahitalic) format("woff"), url(blahitalic) format("truetype")'
529 }],
530 },
531
532 anotherTest: {
533 fontFamily: [{
534 fontStyle: "normal",
535 fontWeight: "normal",
536 fontFamily: "My Font",
537 src: 'url(blah) format("woff"), url(blah) format("truetype")'
538 }, {
539 fontStyle: "normal",
540 fontWeight: "normal",
541 fontFamily: "My Other Font",
542 src: 'url(other-font) format("woff"), url(other-font) format("truetype")',
543 }],
544 },
545 });
546
547 const render = () => {
548 css(fontSheet.test);
549 css(fontSheet.anotherTest);
550 return "html!";
551 };
552
553 const ret = StyleSheetServer.renderStatic(render);
554
555 // 3 unique @font-faces should be added
556 assert.equal(3, ret.css.content.match(/@font-face/g).length);
557
558 assert.include(ret.css.content, "font-style:normal");
559 assert.include(ret.css.content, "font-style:italic");
560
561 assert.include(ret.css.content, 'font-family:"My Font"');
562 assert.include(ret.css.content, 'font-family:"My Font","My Other Font"');
563 });
564});
565
566describe('StyleSheetTestUtils.suppressStyleInjection', () => {
567 beforeEach(() => {
568 StyleSheetTestUtils.suppressStyleInjection();
569 });
570
571 afterEach(() => {
572 StyleSheetTestUtils.clearBufferAndResumeStyleInjection();
573 });
574
575 it('allows css to be called without requiring a DOM', (done) => {
576 const sheet = StyleSheet.create({
577 red: {
578 color: 'red',
579 },
580 });
581
582 css(sheet.red);
583 asap(done);
584 });
585});
586
587describe('StyleSheetTestUtils.getBufferedStyles', () => {
588 beforeEach(() => {
589 StyleSheetTestUtils.suppressStyleInjection();
590 });
591
592 afterEach(() => {
593 StyleSheetTestUtils.clearBufferAndResumeStyleInjection();
594 });
595
596 it('returns injection buffer', () => {
597 const sheet = StyleSheet.create({
598 red: {
599 color: 'red',
600 },
601 });
602 css(sheet.red);
603 asap(() => {
604 const buffer = StyleSheetTestUtils.getBufferedStyles();
605 assert.equal(buffer.length, 1);
606 assert.include(buffer[0], 'color:red');
607 assert.include(buffer[0], sheet.red._name);
608 })
609 });
610});