OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library declaration_test; |
| 6 |
| 7 import 'package:unittest/unittest.dart'; |
| 8 import 'testing.dart'; |
| 9 import 'package:csslib/parser.dart'; |
| 10 import 'package:csslib/visitor.dart'; |
| 11 |
| 12 |
| 13 /** CSS compiler options no checks in in memory style sheet. */ |
| 14 List options = ['--no-colors', 'memory']; |
| 15 |
| 16 void testSimpleTerms() { |
| 17 var errors = []; |
| 18 final String input = r''' |
| 19 @ import url("test.css"); |
| 20 .foo { |
| 21 background-color: #191919; |
| 22 width: 10PX; |
| 23 height: 22mM !important; |
| 24 border-width: 20cm; |
| 25 margin-width: 33%; |
| 26 border-height: 30EM; |
| 27 width: .6in; |
| 28 length: 1.2in; |
| 29 -web-stuff: -10Px; |
| 30 }'''; |
| 31 final String generated = r''' |
| 32 @import "test.css"; |
| 33 .foo { |
| 34 background-color: #191919; |
| 35 width: 10px; |
| 36 height: 22mm !important; |
| 37 border-width: 20cm; |
| 38 margin-width: 33%; |
| 39 border-height: 30em; |
| 40 width: .6in; |
| 41 length: 1.2in; |
| 42 -web-stuff: -10px; |
| 43 }'''; |
| 44 |
| 45 var stylesheet = parseCss(input, errors: errors); |
| 46 |
| 47 expect(stylesheet != null, true); |
| 48 expect(errors.isEmpty, true, reason: errors.toString()); |
| 49 expect(prettyPrint(stylesheet), generated); |
| 50 |
| 51 final String input2 = r''' |
| 52 * { |
| 53 border-color: green; |
| 54 }'''; |
| 55 final String generated2 = r''' |
| 56 * { |
| 57 border-color: #008000; |
| 58 }'''; |
| 59 |
| 60 stylesheet = parseCss(input2, errors: errors..clear()); |
| 61 |
| 62 expect(stylesheet != null, true); |
| 63 expect(errors.isEmpty, true, reason: errors.toString()); |
| 64 expect(prettyPrint(stylesheet), generated2); |
| 65 } |
| 66 |
| 67 /** |
| 68 * Declarations with comments, references with single-quotes, double-quotes, |
| 69 * no quotes. Hex values with # and letters, and functions (rgba, url, etc.) |
| 70 */ |
| 71 void testDeclarations() { |
| 72 var errors = []; |
| 73 final String input = r''' |
| 74 .more { |
| 75 color: white; |
| 76 color: black; |
| 77 color: cyan; |
| 78 color: red; |
| 79 color: #aabbcc; /* test -- 3 */ |
| 80 color: blue; |
| 81 background-image: url(http://test.jpeg); |
| 82 background-image: url("http://double_quote.html"); |
| 83 background-image: url('http://single_quote.html'); |
| 84 color: rgba(10,20,255); <!-- test CDO/CDC --> |
| 85 color: #123aef; /* hex # part integer and part identifier */ |
| 86 }'''; |
| 87 final String generated = r''' |
| 88 .more { |
| 89 color: #fff; |
| 90 color: #000; |
| 91 color: #0ff; |
| 92 color: #f00; |
| 93 color: #abc; |
| 94 color: #00f; |
| 95 background-image: url("http://test.jpeg"); |
| 96 background-image: url("http://double_quote.html"); |
| 97 background-image: url("http://single_quote.html"); |
| 98 color: rgba(10, 20, 255); |
| 99 color: #123aef; |
| 100 }'''; |
| 101 |
| 102 var stylesheet = parseCss(input, errors: errors); |
| 103 |
| 104 expect(stylesheet != null, true); |
| 105 expect(errors.isEmpty, true, reason: errors.toString()); |
| 106 expect(prettyPrint(stylesheet), generated); |
| 107 } |
| 108 |
| 109 void testIdentifiers() { |
| 110 var errors = []; |
| 111 final String input = r''' |
| 112 #da { |
| 113 height: 100px; |
| 114 } |
| 115 #foo { |
| 116 width: 10px; |
| 117 color: #ff00cc; |
| 118 } |
| 119 '''; |
| 120 final String generated = r''' |
| 121 #da { |
| 122 height: 100px; |
| 123 } |
| 124 #foo { |
| 125 width: 10px; |
| 126 color: #f0c; |
| 127 }'''; |
| 128 |
| 129 var stylesheet = parseCss(input, errors: errors); |
| 130 |
| 131 expect(errors.isEmpty, true, reason: errors.toString()); |
| 132 expect(stylesheet != null, true); |
| 133 expect(prettyPrint(stylesheet), generated); |
| 134 } |
| 135 |
| 136 void testComposites() { |
| 137 var errors = []; |
| 138 final String input = r''' |
| 139 .xyzzy { |
| 140 border: 10px 80px 90px 100px; |
| 141 width: 99%; |
| 142 } |
| 143 @-webkit-keyframes pulsate { |
| 144 0% { |
| 145 -webkit-transform: translate3d(0, 0, 0) scale(1.0); |
| 146 } |
| 147 }'''; |
| 148 final String generated = r''' |
| 149 .xyzzy { |
| 150 border: 10px 80px 90px 100px; |
| 151 width: 99%; |
| 152 } |
| 153 @-webkit-keyframes pulsate { |
| 154 0% { |
| 155 -webkit-transform: translate3d(0, 0, 0) scale(1.0); |
| 156 } |
| 157 }'''; |
| 158 |
| 159 var stylesheet = parseCss(input, errors: errors); |
| 160 expect(stylesheet != null, true); |
| 161 expect(errors.isEmpty, true, reason: errors.toString()); |
| 162 expect(prettyPrint(stylesheet), generated); |
| 163 } |
| 164 |
| 165 void testUnits() { |
| 166 var errors = []; |
| 167 final String input = r''' |
| 168 #id-1 { |
| 169 transition: color 0.4s; |
| 170 animation-duration: 500ms; |
| 171 top: 1em; |
| 172 left: 200ex; |
| 173 right: 300px; |
| 174 bottom: 400cm; |
| 175 border-width: 2.5mm; |
| 176 margin-top: .5in; |
| 177 margin-left: 5pc; |
| 178 margin-right: 5ex; |
| 179 margin-bottom: 5ch; |
| 180 font-size: 10pt; |
| 181 padding-top: 22rem; |
| 182 padding-left: 33vw; |
| 183 padding-right: 34vh; |
| 184 padding-bottom: 3vmin; |
| 185 transform: rotate(20deg); |
| 186 voice-pitch: 10hz; |
| 187 } |
| 188 #id-2 { |
| 189 left: 2fr; |
| 190 font-size: 10vmax; |
| 191 transform: rotatex(20rad); |
| 192 voice-pitch: 10khz; |
| 193 -web-kit-resolution: 2dpi; /* Bogus property name testing dpi unit. */ |
| 194 } |
| 195 #id-3 { |
| 196 -web-kit-resolution: 3dpcm; /* Bogus property name testing dpi unit. */ |
| 197 transform: rotatey(20grad); |
| 198 } |
| 199 #id-4 { |
| 200 -web-kit-resolution: 4dppx; /* Bogus property name testing dpi unit. */ |
| 201 transform: rotatez(20turn); |
| 202 } |
| 203 '''; |
| 204 |
| 205 final String generated = r''' |
| 206 #id-1 { |
| 207 transition: color 0.4s; |
| 208 animation-duration: 500ms; |
| 209 top: 1em; |
| 210 left: 200ex; |
| 211 right: 300px; |
| 212 bottom: 400cm; |
| 213 border-width: 2.5mm; |
| 214 margin-top: .5in; |
| 215 margin-left: 5pc; |
| 216 margin-right: 5ex; |
| 217 margin-bottom: 5ch; |
| 218 font-size: 10pt; |
| 219 padding-top: 22rem; |
| 220 padding-left: 33vw; |
| 221 padding-right: 34vh; |
| 222 padding-bottom: 3vmin; |
| 223 transform: rotate(20deg); |
| 224 voice-pitch: 10hz; |
| 225 } |
| 226 #id-2 { |
| 227 left: 2fr; |
| 228 font-size: 10vmax; |
| 229 transform: rotatex(20rad); |
| 230 voice-pitch: 10khz; |
| 231 -web-kit-resolution: 2dpi; |
| 232 } |
| 233 #id-3 { |
| 234 -web-kit-resolution: 3dpcm; |
| 235 transform: rotatey(20grad); |
| 236 } |
| 237 #id-4 { |
| 238 -web-kit-resolution: 4dppx; |
| 239 transform: rotatez(20turn); |
| 240 }'''; |
| 241 |
| 242 var stylesheet = parseCss(input, errors: errors, opts: options); |
| 243 |
| 244 expect(stylesheet != null, true); |
| 245 expect(errors.isEmpty, true, reason: errors.toString()); |
| 246 expect(prettyPrint(stylesheet), generated); |
| 247 } |
| 248 |
| 249 void testUnicode() { |
| 250 var errors = []; |
| 251 final String input = r''' |
| 252 .toggle:after { |
| 253 content: '✔'; |
| 254 line-height: 43px; |
| 255 font-size: 20px; |
| 256 color: #d9d9d9; |
| 257 text-shadow: 0 -1px 0 #bfbfbf; |
| 258 } |
| 259 '''; |
| 260 |
| 261 final String generated = r''' |
| 262 .toggle:after { |
| 263 content: "✔"; |
| 264 line-height: 43px; |
| 265 font-size: 20px; |
| 266 color: #d9d9d9; |
| 267 text-shadow: 0 -1px 0 #bfbfbf; |
| 268 }'''; |
| 269 |
| 270 var stylesheet = parseCss(input, errors: errors); |
| 271 |
| 272 expect(stylesheet != null, true); |
| 273 expect(errors.isEmpty, true, reason: errors.toString()); |
| 274 expect(prettyPrint(stylesheet), generated); |
| 275 } |
| 276 |
| 277 void testNewerCss() { |
| 278 var errors = []; |
| 279 final String input = r''' |
| 280 @media screen,print { |
| 281 .foobar_screen { |
| 282 width: 10px; |
| 283 } |
| 284 } |
| 285 @page { |
| 286 height: 22px; |
| 287 size: 3in 3in; |
| 288 } |
| 289 @page : left { |
| 290 width: 10px; |
| 291 } |
| 292 @page bar : left { @top-left { margin: 8px; } } |
| 293 @charset "ISO-8859-1"; |
| 294 @charset 'ASCII';'''; |
| 295 |
| 296 final String generated = r''' |
| 297 @media screen, print { |
| 298 .foobar_screen { |
| 299 width: 10px; |
| 300 } |
| 301 } |
| 302 @page { |
| 303 height: 22px; |
| 304 size: 3in 3in; |
| 305 } |
| 306 @page:left { |
| 307 width: 10px; |
| 308 } |
| 309 @page bar:left { |
| 310 @top-left { |
| 311 margin: 8px; |
| 312 } |
| 313 } |
| 314 @charset "ISO-8859-1"; |
| 315 @charset "ASCII";'''; |
| 316 |
| 317 var stylesheet = parseCss(input, errors: errors); |
| 318 |
| 319 expect(stylesheet != null, true); |
| 320 expect(errors.isEmpty, true, reason: errors.toString()); |
| 321 expect(prettyPrint(stylesheet), generated); |
| 322 } |
| 323 |
| 324 void testMediaQueries() { |
| 325 var errors = []; |
| 326 String input = ''' |
| 327 @media screen and (-webkit-min-device-pixel-ratio:0) { |
| 328 .todo-item .toggle { |
| 329 background: none; |
| 330 } |
| 331 #todo-item .toggle { |
| 332 height: 40px; |
| 333 } |
| 334 }'''; |
| 335 String generated = ''' |
| 336 @media screen AND (-webkit-min-device-pixel-ratio:0) { |
| 337 .todo-item .toggle { |
| 338 background: none; |
| 339 } |
| 340 #todo-item .toggle { |
| 341 height: 40px; |
| 342 } |
| 343 }'''; |
| 344 |
| 345 var stylesheet = parseCss(input, errors: errors, opts: options); |
| 346 expect(stylesheet != null, true); |
| 347 expect(errors.isEmpty, true, reason: errors.toString()); |
| 348 expect(prettyPrint(stylesheet), generated); |
| 349 |
| 350 input = ''' |
| 351 @media handheld and (min-width: 20em), |
| 352 screen and (min-width: 20em) { |
| 353 #id { color: red; } |
| 354 .myclass { height: 20px; } |
| 355 } |
| 356 @media print and (min-resolution: 300dpi) { |
| 357 #anotherId { |
| 358 color: #fff; |
| 359 } |
| 360 } |
| 361 @media print and (min-resolution: 280dpcm) { |
| 362 #finalId { |
| 363 color: #aaa; |
| 364 } |
| 365 .class2 { |
| 366 border: 20px; |
| 367 } |
| 368 }'''; |
| 369 generated = |
| 370 '''@media handheld AND (min-width:20em), screen AND (min-width:20em) { |
| 371 #id { |
| 372 color: #f00; |
| 373 } |
| 374 .myclass { |
| 375 height: 20px; |
| 376 } |
| 377 } @media print AND (min-resolution:300dpi) { |
| 378 #anotherId { |
| 379 color: #fff; |
| 380 } |
| 381 } @media print AND (min-resolution:280dpcm) { |
| 382 #finalId { |
| 383 color: #aaa; |
| 384 } |
| 385 .class2 { |
| 386 border: 20px; |
| 387 } |
| 388 }'''; |
| 389 |
| 390 stylesheet = parseCss(input, errors: errors..clear(), opts: options); |
| 391 |
| 392 expect(stylesheet != null, true); |
| 393 expect(errors.isEmpty, true, reason: errors.toString()); |
| 394 expect(prettyPrint(stylesheet), generated); |
| 395 |
| 396 input = ''' |
| 397 @media only screen and (min-device-width: 4000px) and |
| 398 (min-device-height: 2000px), screen (another: 100px) { |
| 399 html { |
| 400 font-size: 10em; |
| 401 } |
| 402 }'''; |
| 403 generated = '@media ONLY screen AND (min-device-width:4000px) ' |
| 404 'AND (min-device-height:2000px), screen (another:100px) {\n' |
| 405 'html {\n font-size: 10em;\n}\n}'; |
| 406 |
| 407 stylesheet = parseCss(input, errors: errors..clear(), opts: options); |
| 408 |
| 409 expect(stylesheet != null, true); |
| 410 expect(errors.isEmpty, true, reason: errors.toString()); |
| 411 expect(prettyPrint(stylesheet), generated); |
| 412 |
| 413 input = ''' |
| 414 @media screen,print (min-device-width: 4000px) and |
| 415 (min-device-height: 2000px), screen (another: 100px) { |
| 416 html { |
| 417 font-size: 10em; |
| 418 } |
| 419 }'''; |
| 420 generated = '@media screen, print (min-device-width:4000px) AND ' |
| 421 '(min-device-height:2000px), screen (another:100px) {\n' |
| 422 'html {\n font-size: 10em;\n}\n}'; |
| 423 |
| 424 stylesheet = parseCss(input, errors: errors..clear(), opts: options); |
| 425 |
| 426 expect(stylesheet != null, true); |
| 427 expect(errors.isEmpty, true, reason: errors.toString()); |
| 428 expect(prettyPrint(stylesheet), generated); |
| 429 |
| 430 input = ''' |
| 431 @import "test.css" ONLY screen, NOT print (min-device-width: 4000px);'''; |
| 432 generated = |
| 433 '@import "test.css" ONLY screen, NOT print (min-device-width:4000px);'; |
| 434 |
| 435 stylesheet = parseCss(input, errors: errors..clear(), opts: options); |
| 436 |
| 437 expect(stylesheet != null, true); |
| 438 expect(errors.isEmpty, true, reason: errors.toString()); |
| 439 expect(prettyPrint(stylesheet), generated); |
| 440 } |
| 441 |
| 442 void testFontFace() { |
| 443 var errors = []; |
| 444 |
| 445 final String input = ''' |
| 446 @font-face { |
| 447 font-family: BBCBengali; |
| 448 src: url(fonts/BBCBengali.ttf) format("opentype"); |
| 449 unicode-range: U+0A-FF, U+980-9FF, U+????, U+3???; |
| 450 }'''; |
| 451 final String generated = '''@font-face { |
| 452 font-family: BBCBengali; |
| 453 src: url("fonts/BBCBengali.ttf") format("opentype"); |
| 454 unicode-range: U+0A-FF, U+980-9FF, U+????, U+3???; |
| 455 }'''; |
| 456 var stylesheet = parseCss(input, errors: errors, opts: options); |
| 457 |
| 458 expect(stylesheet != null, true); |
| 459 expect(errors.isEmpty, true, reason: errors.toString()); |
| 460 expect(prettyPrint(stylesheet), generated); |
| 461 |
| 462 final String input1 = ''' |
| 463 @font-face { |
| 464 font-family: Gentium; |
| 465 src: url(http://example.com/fonts/Gentium.ttf); |
| 466 src: url(http://example.com/fonts/Gentium.ttf); |
| 467 }'''; |
| 468 final String generated1 = '''@font-face { |
| 469 font-family: Gentium; |
| 470 src: url("http://example.com/fonts/Gentium.ttf"); |
| 471 src: url("http://example.com/fonts/Gentium.ttf"); |
| 472 }'''; |
| 473 |
| 474 stylesheet = parseCss(input1, errors: errors..clear(), opts: options); |
| 475 |
| 476 expect(stylesheet != null, true); |
| 477 expect(errors.isEmpty, true, reason: errors.toString()); |
| 478 expect(prettyPrint(stylesheet), generated1); |
| 479 |
| 480 final String input2 = ''' |
| 481 @font-face { |
| 482 src: url(ideal-sans-serif.woff) format("woff"), |
| 483 url(basic-sans-serif.ttf) format("opentype"), |
| 484 local(Gentium Bold); |
| 485 }'''; |
| 486 final String generated2 = |
| 487 '@font-face {\n' |
| 488 ' src: url("ideal-sans-serif.woff") ' |
| 489 'format("woff"), url("basic-sans-serif.ttf") ' |
| 490 'format("opentype"), local(Gentium Bold);\n}'; |
| 491 |
| 492 stylesheet = parseCss(input2, errors: errors..clear(), opts: options); |
| 493 |
| 494 expect(stylesheet != null, true); |
| 495 expect(errors.isEmpty, true, reason: errors.toString()); |
| 496 expect(prettyPrint(stylesheet), generated2); |
| 497 |
| 498 final String input3 = '''@font-face { |
| 499 font-family: MyGentium Text Ornaments; |
| 500 src: local(Gentium Bold), /* full font name */ |
| 501 local(Gentium-Bold), /* Postscript name */ |
| 502 url(GentiumBold.ttf); /* otherwise, download it */ |
| 503 font-weight: bold; |
| 504 }'''; |
| 505 final String generated3 = '''@font-face { |
| 506 font-family: MyGentium Text Ornaments; |
| 507 src: local(Gentium Bold), local(Gentium-Bold), url("GentiumBold.ttf"); |
| 508 font-weight: bold; |
| 509 }'''; |
| 510 |
| 511 stylesheet = parseCss(input3, errors: errors..clear(), opts: options); |
| 512 |
| 513 expect(stylesheet != null, true); |
| 514 expect(errors.isEmpty, true, reason: errors.toString()); |
| 515 expect(prettyPrint(stylesheet), generated3); |
| 516 |
| 517 final String input4 = ''' |
| 518 @font-face { |
| 519 font-family: STIXGeneral; |
| 520 src: local(STIXGeneral), url(/stixfonts/STIXGeneral.otf); |
| 521 unicode-range: U+000-49F, U+2000-27FF, U+2900-2BFF, U+1D400-1D7FF; |
| 522 }'''; |
| 523 final String generated4 = '''@font-face { |
| 524 font-family: STIXGeneral; |
| 525 src: local(STIXGeneral), url("/stixfonts/STIXGeneral.otf"); |
| 526 unicode-range: U+000-49F, U+2000-27FF, U+2900-2BFF, U+1D400-1D7FF; |
| 527 }'''; |
| 528 stylesheet = parseCss(input4, errors: errors..clear(), opts: options); |
| 529 |
| 530 expect(stylesheet != null, true); |
| 531 expect(errors.isEmpty, true, reason: errors.toString()); |
| 532 expect(prettyPrint(stylesheet), generated4); |
| 533 } |
| 534 |
| 535 void testCssFile() { |
| 536 var errors = []; |
| 537 final String input = r''' |
| 538 @import 'simple.css' |
| 539 @import "test.css" print |
| 540 @import url(test.css) screen, print |
| 541 @import url(http://google.com/maps/maps.css); |
| 542 |
| 543 div[href^='test'] { |
| 544 height: 10px; |
| 545 } |
| 546 |
| 547 @-webkit-keyframes pulsate { |
| 548 from { |
| 549 -webkit-transform: translate3d(0, 0, 0) scale(1.0); |
| 550 } |
| 551 10% { |
| 552 -webkit-transform: translate3d(0, 0, 0) scale(1.0); |
| 553 } |
| 554 30% { |
| 555 -webkit-transform: translate3d(0, 2, 0) scale(1.0); |
| 556 } |
| 557 } |
| 558 |
| 559 .foobar { |
| 560 grid-columns: 10px ("content" 1fr 10px)[4]; |
| 561 } |
| 562 |
| 563 .test-background { |
| 564 background: url(http://www.foo.com/bar.png); |
| 565 } |
| 566 '''; |
| 567 |
| 568 final String generated = |
| 569 '@import "simple.css"; ' |
| 570 '@import "test.css" print; ' |
| 571 '@import "test.css" screen, print; ' |
| 572 '@import "http://google.com/maps/maps.css";\n' |
| 573 'div[href^="test"] {\n' |
| 574 ' height: 10px;\n' |
| 575 '}\n' |
| 576 '@-webkit-keyframes pulsate {\n' |
| 577 ' from {\n' |
| 578 ' -webkit-transform: translate3d(0, 0, 0) scale(1.0);\n' |
| 579 ' }\n' |
| 580 ' 10% {\n' |
| 581 ' -webkit-transform: translate3d(0, 0, 0) scale(1.0);\n' |
| 582 ' }\n' |
| 583 ' 30% {\n' |
| 584 ' -webkit-transform: translate3d(0, 2, 0) scale(1.0);\n' |
| 585 ' }\n' |
| 586 '}\n' |
| 587 '.foobar {\n' |
| 588 ' grid-columns: 10px ("content" 1fr 10px) [4];\n' |
| 589 '}\n' |
| 590 '.test-background {\n' |
| 591 ' background: url("http://www.foo.com/bar.png");\n' |
| 592 '}'; |
| 593 var stylesheet = parseCss(input, errors: errors); |
| 594 |
| 595 expect(stylesheet != null, true); |
| 596 expect(errors.isEmpty, true, reason: errors.toString()); |
| 597 expect(prettyPrint(stylesheet), generated); |
| 598 } |
| 599 |
| 600 void testCompactEmitter() { |
| 601 var errors = []; |
| 602 |
| 603 // Check !import compactly emitted. |
| 604 final String input = r''' |
| 605 div { |
| 606 color: green !important; |
| 607 } |
| 608 '''; |
| 609 final String generated = "div { color: green!important; }"; |
| 610 |
| 611 var stylesheet = parseCss(input, errors: errors); |
| 612 |
| 613 expect(stylesheet != null, true); |
| 614 expect(errors.isEmpty, true, reason: errors.toString()); |
| 615 expect(compactOuptut(stylesheet), generated); |
| 616 |
| 617 // Check namespace directive compactly emitted. |
| 618 final String input2 = "@namespace a url(http://www.example.org/a);"; |
| 619 final String generated2 = "@namespace a url(http://www.example.org/a);"; |
| 620 |
| 621 var stylesheet2 = parseCss(input2, errors: errors..clear()); |
| 622 |
| 623 expect(stylesheet2 != null, true); |
| 624 expect(errors.isEmpty, true, reason: errors.toString()); |
| 625 expect(compactOuptut(stylesheet2), generated2); |
| 626 } |
| 627 |
| 628 void testNotSelectors() { |
| 629 var errors = []; |
| 630 |
| 631 final String input = r''' |
| 632 .details:not(.open-details) x-element, |
| 633 .details:not(.open-details) .summary { |
| 634 overflow: hidden; |
| 635 } |
| 636 |
| 637 .details:not(.open-details) x-icon { |
| 638 margin-left: 99px; |
| 639 } |
| 640 |
| 641 .kind-class .details:not(.open-details) x-icon { |
| 642 margin-left: 0px; |
| 643 } |
| 644 |
| 645 .name { |
| 646 margin-left: 0px; |
| 647 } |
| 648 |
| 649 .details:not(.open-details) .the-class { |
| 650 width: 80px; |
| 651 } |
| 652 |
| 653 *:focus |
| 654 { |
| 655 outline: none; |
| 656 } |
| 657 |
| 658 body > h2:not(:first-of-type):not(:last-of-type) { |
| 659 color: red; |
| 660 } |
| 661 |
| 662 .details-1:not([DISABLED]) { |
| 663 outline: none; |
| 664 } |
| 665 |
| 666 html|*:not(:link):not(:visited) { |
| 667 width: 92%; |
| 668 } |
| 669 |
| 670 *|*:not(*) { |
| 671 font-weight: bold; |
| 672 } |
| 673 |
| 674 *:not(:not([disabled])) { color: blue; } |
| 675 '''; |
| 676 final String generated = r''' |
| 677 .details:not(.open-details) x-element, .details:not(.open-details) .summary { |
| 678 overflow: hidden; |
| 679 } |
| 680 .details:not(.open-details) x-icon { |
| 681 margin-left: 99px; |
| 682 } |
| 683 .kind-class .details:not(.open-details) x-icon { |
| 684 margin-left: 0px; |
| 685 } |
| 686 .name { |
| 687 margin-left: 0px; |
| 688 } |
| 689 .details:not(.open-details) .the-class { |
| 690 width: 80px; |
| 691 } |
| 692 *:focus { |
| 693 outline: none; |
| 694 } |
| 695 body > h2:not(:first-of-type):not(:last-of-type) { |
| 696 color: #f00; |
| 697 } |
| 698 .details-1:not([DISABLED]) { |
| 699 outline: none; |
| 700 } |
| 701 html|*:not(:link):not(:visited) { |
| 702 width: 92%; |
| 703 } |
| 704 *|*:not(*) { |
| 705 font-weight: bold; |
| 706 } |
| 707 *:not(:not([disabled])) { |
| 708 color: #00f; |
| 709 }'''; |
| 710 |
| 711 var stylesheet = parseCss(input, errors: errors, opts: options); |
| 712 |
| 713 expect(stylesheet != null, true); |
| 714 expect(errors.isEmpty, true, reason: errors.toString()); |
| 715 expect(prettyPrint(stylesheet), generated); |
| 716 } |
| 717 |
| 718 void testIE() { |
| 719 var errors = []; |
| 720 final String input = |
| 721 ".test {\n" |
| 722 " filter: progid:DXImageTransform.Microsoft.gradient" |
| 723 "(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');\n" |
| 724 "}"; |
| 725 final String generated = |
| 726 ".test {\n" |
| 727 " filter: progid:DXImageTransform.Microsoft.gradient" |
| 728 "(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');\n" |
| 729 "}"; |
| 730 |
| 731 var stylesheet = parseCss(input, errors: errors, opts: options); |
| 732 |
| 733 expect(stylesheet != null, true); |
| 734 expect(errors.isEmpty, true, reason: errors.toString()); |
| 735 expect(prettyPrint(stylesheet), generated); |
| 736 |
| 737 final String input2 = |
| 738 ".test {\n" |
| 739 " filter: progid:DXImageTransform.Microsoft.gradient" |
| 740 "(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670')\n" |
| 741 " progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);\n" |
| 742 "}"; |
| 743 |
| 744 final String generated2 = |
| 745 ".test {\n" |
| 746 " filter: progid:DXImageTransform.Microsoft.gradient" |
| 747 "(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670')\n" |
| 748 " progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);\n" |
| 749 "}"; |
| 750 |
| 751 stylesheet = parseCss(input2, errors: errors..clear(), opts: options); |
| 752 |
| 753 expect(stylesheet != null, true); |
| 754 expect(errors.isEmpty, true, reason: errors.toString()); |
| 755 expect(prettyPrint(stylesheet), generated2); |
| 756 |
| 757 final String input3 = ''' |
| 758 div { |
| 759 filter: alpha(opacity=80); /* IE7 and under */ |
| 760 -ms-filter: "Alpha(Opacity=40)"; /* IE8 and newer */ |
| 761 |
| 762 Filter: Blur(Add = 0, Direction = 225, Strength = 10); |
| 763 Filter: FlipV; |
| 764 Filter: Gray; |
| 765 FILTER: Chroma(Color = #000000) Mask(Color=#00FF00); |
| 766 Filter: Alpha(Opacity=100, FinishOpacity=0, Style=2, StartX=20, StartY=40, |
| 767 FinishX=0, FinishY=0) Wave(Add=0, Freq=5, LightStrength=20, |
| 768 Phase=220, Strength=10); |
| 769 } |
| 770 '''; |
| 771 final String generated3 = 'div {\n filter: alpha(opacity=80);\n' |
| 772 ' -ms-filter: "Alpha(Opacity=40)";\n' |
| 773 ' Filter: Blur(Add = 0, Direction = 225, Strength = 10);\n' |
| 774 ' Filter: FlipV;\n Filter: Gray;\n' |
| 775 ' FILTER: Chroma(Color = #000000) Mask(Color=#00FF00);\n' |
| 776 ' Filter: Alpha(Opacity=100, FinishOpacity=0, Style=2, ' |
| 777 'StartX=20, StartY=40, \n' |
| 778 ' FinishX=0, FinishY=0) Wave(Add=0, Freq=5, LightStrength=20, \n' |
| 779 ' Phase=220, Strength=10);\n}'; |
| 780 |
| 781 stylesheet = parseCss(input3, errors: errors..clear(), opts: options); |
| 782 |
| 783 expect(stylesheet != null, true); |
| 784 expect(errors.isEmpty, true, reason: errors.toString()); |
| 785 expect(prettyPrint(stylesheet), generated3); |
| 786 } |
| 787 |
| 788 /** |
| 789 * Test IE specific declaration syntax: |
| 790 * IE6 property name prefixed with _ (normal CSS property name can start |
| 791 * with an underscore). |
| 792 * |
| 793 * IE7 or below property add asterisk before the CSS property. |
| 794 * |
| 795 * IE8 or below add \9 at end of declaration expression e.g., |
| 796 * background: red\9; |
| 797 */ |
| 798 void testIEDeclaration() { |
| 799 var errors = []; |
| 800 |
| 801 final input = ''' |
| 802 .testIE-6 { |
| 803 _zoom : 5; |
| 804 } |
| 805 .clearfix { |
| 806 *zoom: 1; |
| 807 } |
| 808 audio, video { |
| 809 display: inline-block; |
| 810 *display: inline; |
| 811 *zoom: 1; |
| 812 } |
| 813 input { |
| 814 *overflow: visible; |
| 815 line-height: normal; |
| 816 } |
| 817 .uneditable-input:focus { |
| 818 border-color: rgba(82, 168, 236, 0.8); |
| 819 outline: 0; |
| 820 outline: thin dotted \\9; /* IE6-9 */ |
| 821 } |
| 822 |
| 823 input[type="radio"], input[type="checkbox"] { |
| 824 margin-top: 1px \\9; |
| 825 *margin-top: 0; |
| 826 } |
| 827 |
| 828 input.search-query { |
| 829 padding-right: 14px; |
| 830 padding-right: 4px \\9; |
| 831 padding-left: 14px; |
| 832 padding-left: 4px \\9; /* IE7-8 no border-radius, don't indent padding. */ |
| 833 } |
| 834 |
| 835 .btn.active { |
| 836 background-color: #cccccc \\9; |
| 837 } |
| 838 |
| 839 @-webkit-keyframes progress-bar-stripes { |
| 840 from { |
| 841 background-position: 40px 0; |
| 842 } |
| 843 to { |
| 844 background-position: 0 0; |
| 845 } |
| 846 } |
| 847 |
| 848 @-moz-keyframes progress-bar-stripes { |
| 849 from { |
| 850 background-position: 40px 0; |
| 851 } |
| 852 to { |
| 853 background-position: 0 0; |
| 854 } |
| 855 } |
| 856 |
| 857 @-ms-keyframes progress-bar-stripes { |
| 858 from { |
| 859 background-position: 40px 0; |
| 860 } |
| 861 to { |
| 862 background-position: 0 0; |
| 863 } |
| 864 } |
| 865 |
| 866 @-o-keyframes progress-bar-stripes { |
| 867 from { |
| 868 background-position: 40px 0; |
| 869 } |
| 870 to { |
| 871 background-position: 0 0; |
| 872 } |
| 873 } |
| 874 |
| 875 @keyframes progress-bar-stripes { |
| 876 from { |
| 877 background-position: 40px 0; |
| 878 } |
| 879 to { |
| 880 background-position: 0 0; |
| 881 } |
| 882 }'''; |
| 883 |
| 884 final generated = '''.testIE-6 { |
| 885 _zoom: 5; |
| 886 } |
| 887 .clearfix { |
| 888 *zoom: 1; |
| 889 } |
| 890 audio, video { |
| 891 display: inline-block; |
| 892 *display: inline; |
| 893 *zoom: 1; |
| 894 } |
| 895 input { |
| 896 *overflow: visible; |
| 897 line-height: normal; |
| 898 } |
| 899 .uneditable-input:focus { |
| 900 border-color: rgba(82, 168, 236, 0.8); |
| 901 outline: 0; |
| 902 outline: thin dotted \\9; |
| 903 } |
| 904 input[type="radio"], input[type="checkbox"] { |
| 905 margin-top: 1px \\9; |
| 906 *margin-top: 0; |
| 907 } |
| 908 input.search-query { |
| 909 padding-right: 14px; |
| 910 padding-right: 4px \\9; |
| 911 padding-left: 14px; |
| 912 padding-left: 4px \\9; |
| 913 } |
| 914 .btn.active { |
| 915 background-color: #ccc \\9; |
| 916 } |
| 917 @-webkit-keyframes progress-bar-stripes { |
| 918 from { |
| 919 background-position: 40px 0; |
| 920 } |
| 921 to { |
| 922 background-position: 0 0; |
| 923 } |
| 924 } |
| 925 @-moz-keyframes progress-bar-stripes { |
| 926 from { |
| 927 background-position: 40px 0; |
| 928 } |
| 929 to { |
| 930 background-position: 0 0; |
| 931 } |
| 932 } |
| 933 @keyframes progress-bar-stripes { |
| 934 from { |
| 935 background-position: 40px 0; |
| 936 } |
| 937 to { |
| 938 background-position: 0 0; |
| 939 } |
| 940 } |
| 941 @-o-keyframes progress-bar-stripes { |
| 942 from { |
| 943 background-position: 40px 0; |
| 944 } |
| 945 to { |
| 946 background-position: 0 0; |
| 947 } |
| 948 } |
| 949 @keyframes progress-bar-stripes { |
| 950 from { |
| 951 background-position: 40px 0; |
| 952 } |
| 953 to { |
| 954 background-position: 0 0; |
| 955 } |
| 956 }'''; |
| 957 |
| 958 var stylesheet = parseCss(input, errors: errors, opts: options); |
| 959 expect(stylesheet != null, true); |
| 960 expect(errors.isEmpty, true, reason: errors.toString()); |
| 961 expect(prettyPrint(stylesheet), generated); |
| 962 } |
| 963 |
| 964 void testHangs() { |
| 965 final optionErrors = ['--no-colors', '--warnings_as_errors', 'memory']; |
| 966 var errors = []; |
| 967 |
| 968 // Bad hexvalue had caused a hang in processTerm. |
| 969 final input = r'''#a { color: #ebebeburl(0/IE8+9+); }'''; |
| 970 var stylesheet = parseCss(input, errors: errors, opts: optionErrors); |
| 971 |
| 972 expect(stylesheet != null, true); |
| 973 expect(errors.length, 3, reason: errors.toString()); |
| 974 |
| 975 var errorMessage = errors[0]; |
| 976 expect(errorMessage.message, contains('Bad hex number')); |
| 977 expect(errorMessage.span, isNotNull); |
| 978 expect(errorMessage.span.start.line, 0); |
| 979 expect(errorMessage.span.start.column, 12); |
| 980 expect(errorMessage.span.text, '#ebebeburl'); |
| 981 |
| 982 errorMessage = errors[1]; |
| 983 expect(errorMessage.message, contains('expected }, but found +')); |
| 984 expect(errorMessage.span, isNotNull); |
| 985 expect(errorMessage.span.start.line, 0); |
| 986 expect(errorMessage.span.start.column, 30); |
| 987 expect(errorMessage.span.text, '+'); |
| 988 |
| 989 errorMessage = errors[2]; |
| 990 expect(errorMessage.message, contains('premature end of file unknown CSS')); |
| 991 expect(errorMessage.span, isNotNull); |
| 992 expect(errorMessage.span.start.line, 0); |
| 993 expect(errorMessage.span.start.column, 31); |
| 994 expect(errorMessage.span.text, ')'); |
| 995 |
| 996 // Missing closing parenthesis for keyframes. |
| 997 final input2 = r'''@-ms-keyframes progress-bar-stripes { |
| 998 from { |
| 999 background-position: 40px 0; |
| 1000 } |
| 1001 to { |
| 1002 background-position: 0 0; |
| 1003 } |
| 1004 '''; |
| 1005 |
| 1006 stylesheet = parseCss(input2, errors: errors..clear(), opts: optionErrors); |
| 1007 |
| 1008 expect(stylesheet != null, true); |
| 1009 |
| 1010 expect(errors.length, 1, reason: errors.toString()); |
| 1011 |
| 1012 errorMessage = errors[0]; |
| 1013 expect(errorMessage.message, contains('unexpected end of file')); |
| 1014 expect(errorMessage.span, isNotNull); |
| 1015 expect(errorMessage.span.start.line, 7); |
| 1016 expect(errorMessage.span.start.column, 0); |
| 1017 expect(errorMessage.span.text, ''); |
| 1018 } |
| 1019 |
| 1020 main() { |
| 1021 test('Simple Terms', testSimpleTerms); |
| 1022 test('Declarations', testDeclarations); |
| 1023 test('Identifiers', testIdentifiers); |
| 1024 test('Composites', testComposites); |
| 1025 test('Units', testUnits); |
| 1026 test('Unicode', testUnicode); |
| 1027 test('Newer CSS', testNewerCss); |
| 1028 test('Media Queries', testMediaQueries); |
| 1029 test('Font-Face', testFontFace); |
| 1030 test('CSS file', testCssFile); |
| 1031 test('Compact Emitter', testCompactEmitter); |
| 1032 test('Selector Negation', testNotSelectors); |
| 1033 test('IE stuff', testIE); |
| 1034 test('IE declaration syntax', testIEDeclaration); |
| 1035 test('Hanging bugs', testHangs); |
| 1036 } |
OLD | NEW |