| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, 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 polymer.test.linter_test; | |
| 6 | |
| 7 import 'package:source_maps/span.dart'; | |
| 8 import 'package:polymer/src/linter.dart'; | |
| 9 import 'package:unittest/compact_vm_config.dart'; | |
| 10 import 'package:unittest/unittest.dart'; | |
| 11 | |
| 12 import 'transform/common.dart'; | |
| 13 | |
| 14 void main() { | |
| 15 useCompactVMConfiguration(); | |
| 16 _testLinter('nothing to report', { | |
| 17 'a|web/test.html': '<!DOCTYPE html><html></html>', | |
| 18 }, { | |
| 19 'a|web/test.html.messages': '', | |
| 20 }); | |
| 21 | |
| 22 group('doctype warning', () { | |
| 23 _testLinter('in web', { | |
| 24 'a|web/test.html': '<html></html>', | |
| 25 }, { | |
| 26 'a|web/test.html.messages': | |
| 27 'warning: Unexpected start tag (html). Expected DOCTYPE. ' | |
| 28 '(web/test.html 0 0)', | |
| 29 }); | |
| 30 | |
| 31 _testLinter('in lib', { | |
| 32 'a|lib/test.html': '<html></html>', | |
| 33 }, { | |
| 34 'a|lib/test.html.messages': '', | |
| 35 }); | |
| 36 }); | |
| 37 | |
| 38 group('duplicate polymer-elements,', () { | |
| 39 _testLinter('same file', { | |
| 40 'a|lib/test.html': '''<html> | |
| 41 <polymer-element name="x-a"></polymer-element> | |
| 42 <polymer-element name="x-a"></polymer-element> | |
| 43 </html>'''.replaceAll(' ', ''), | |
| 44 }, { | |
| 45 'a|lib/test.html.messages': | |
| 46 'warning: duplicate definition for custom tag "x-a". ' | |
| 47 '(lib/test.html 1 0)\n' | |
| 48 'warning: duplicate definition for custom tag "x-a" ' | |
| 49 '(second definition). (lib/test.html 2 0)' | |
| 50 }); | |
| 51 | |
| 52 _testLinter('other file', { | |
| 53 'a|lib/b.html': '''<html> | |
| 54 <polymer-element name="x-a"></polymer-element> | |
| 55 </html>'''.replaceAll(' ', ''), | |
| 56 'a|lib/test.html': '''<html> | |
| 57 <link rel="import" href="b.html"> | |
| 58 <polymer-element name="x-a"></polymer-element> | |
| 59 </html>'''.replaceAll(' ', ''), | |
| 60 }, { | |
| 61 'a|lib/test.html.messages': | |
| 62 'warning: duplicate definition for custom tag "x-a". ' | |
| 63 '(lib/b.html 1 0)\n' | |
| 64 'warning: duplicate definition for custom tag "x-a" ' | |
| 65 '(second definition). (lib/test.html 2 0)' | |
| 66 }); | |
| 67 | |
| 68 _testLinter('other package', { | |
| 69 'b|lib/b.html': '''<html> | |
| 70 <polymer-element name="x-a"></polymer-element> | |
| 71 </html>'''.replaceAll(' ', ''), | |
| 72 'a|lib/test.html': '''<html> | |
| 73 <link rel="import" href="packages/b/b.html"> | |
| 74 <polymer-element name="x-a"></polymer-element> | |
| 75 </html>'''.replaceAll(' ', ''), | |
| 76 }, { | |
| 77 'a|lib/test.html.messages': | |
| 78 'warning: duplicate definition for custom tag "x-a". ' | |
| 79 '(package:b/b.html 1 0)\n' | |
| 80 'warning: duplicate definition for custom tag "x-a" ' | |
| 81 '(second definition). (lib/test.html 2 0)' | |
| 82 }); | |
| 83 }); | |
| 84 | |
| 85 _testLinter('bad link-rel tag (href missing)', { | |
| 86 'a|lib/test.html': '''<html> | |
| 87 <link rel="import"> | |
| 88 <link rel="stylesheet"> | |
| 89 <link rel="foo"> | |
| 90 <link rel="import" href=""> | |
| 91 </html>'''.replaceAll(' ', ''), | |
| 92 }, { | |
| 93 'a|lib/test.html.messages': | |
| 94 'warning: link rel="import" missing href. (lib/test.html 1 0)\n' | |
| 95 'warning: link rel="stylesheet" missing href. (lib/test.html 2 0)\n' | |
| 96 'warning: link rel="import" missing href. (lib/test.html 4 0)' | |
| 97 }); | |
| 98 | |
| 99 _testLinter('<element> is not supported', { | |
| 100 'a|lib/test.html': '''<html> | |
| 101 <element name="x-a"></element> | |
| 102 </html>'''.replaceAll(' ', ''), | |
| 103 }, { | |
| 104 'a|lib/test.html.messages': | |
| 105 'warning: <element> elements are not supported, use <polymer-element>' | |
| 106 ' instead (lib/test.html 1 0)' | |
| 107 }); | |
| 108 | |
| 109 _testLinter('do not nest <polymer-element>', { | |
| 110 'a|lib/test.html': '''<html> | |
| 111 <polymer-element name="x-a"> | |
| 112 <template><div> | |
| 113 <polymer-element name="b"></polymer-element> | |
| 114 </div></template> | |
| 115 </polymer-element> | |
| 116 </html>'''.replaceAll(' ', ''), | |
| 117 }, { | |
| 118 'a|lib/test.html.messages': | |
| 119 'error: Nested polymer element definitions are not allowed.' | |
| 120 ' (lib/test.html 3 4)' | |
| 121 }); | |
| 122 | |
| 123 _testLinter('need a name for <polymer-element>', { | |
| 124 'a|lib/test.html': '''<html> | |
| 125 <polymer-element></polymer-element> | |
| 126 </html>'''.replaceAll(' ', ''), | |
| 127 }, { | |
| 128 'a|lib/test.html.messages': | |
| 129 'error: Missing tag name of the custom element. Please include an ' | |
| 130 'attribute like \'name="your-tag-name"\'. (lib/test.html 1 0)' | |
| 131 }); | |
| 132 | |
| 133 _testLinter('name for <polymer-element> should have dashes', { | |
| 134 'a|lib/test.html': '''<html> | |
| 135 <polymer-element name="a"></polymer-element> | |
| 136 </html>'''.replaceAll(' ', ''), | |
| 137 }, { | |
| 138 'a|lib/test.html.messages': | |
| 139 'error: Invalid name "a". Custom element names must have at least one' | |
| 140 ' dash and can\'t be any of the following names: annotation-xml, ' | |
| 141 'color-profile, font-face, font-face-src, font-face-uri, ' | |
| 142 'font-face-format, font-face-name, missing-glyph. (lib/test.html 1 0)' | |
| 143 }); | |
| 144 | |
| 145 _testLinter('extend is a valid element or existing tag', { | |
| 146 'a|lib/test.html': '''<html> | |
| 147 <polymer-element name="x-a" extends="li"></polymer-element> | |
| 148 </html>'''.replaceAll(' ', ''), | |
| 149 }, { | |
| 150 'a|lib/test.html.messages': '' | |
| 151 }); | |
| 152 | |
| 153 _testLinter('extend is a valid element or existing tag', { | |
| 154 'a|lib/test.html': '''<html> | |
| 155 <polymer-element name="x-a" extends="x-b"></polymer-element> | |
| 156 </html>'''.replaceAll(' ', ''), | |
| 157 }, { | |
| 158 'a|lib/test.html.messages': '' | |
| 159 'warning: custom element with name "x-b" not found. ' | |
| 160 '(lib/test.html 1 0)' | |
| 161 }); | |
| 162 | |
| 163 | |
| 164 group('script type matches code', () { | |
| 165 _testLinter('top-level, .dart url', { | |
| 166 'a|lib/test.html': '''<html> | |
| 167 <script src="foo.dart"></script> | |
| 168 </html>'''.replaceAll(' ', ''), | |
| 169 }, { | |
| 170 'a|lib/test.html.messages': | |
| 171 'warning: script tag with .dart source file but no type will be ' | |
| 172 'treated as JavaScript. Did you forget type="application/dart"?' | |
| 173 ' (lib/test.html 1 0)' | |
| 174 }); | |
| 175 | |
| 176 _testLinter('in polymer-element, .dart url', { | |
| 177 'a|lib/test.html': '''<html> | |
| 178 <polymer-element name="x-a"> | |
| 179 <script src="foo.dart"></script> | |
| 180 </polymer-element> | |
| 181 </html>'''.replaceAll(' ', ''), | |
| 182 }, { | |
| 183 'a|lib/test.html.messages': | |
| 184 'warning: script tag with .dart source file but no type will be ' | |
| 185 'treated as JavaScript. Did you forget type="application/dart"?' | |
| 186 ' (lib/test.html 2 0)' | |
| 187 }); | |
| 188 | |
| 189 _testLinter('in polymer-element, .js url', { | |
| 190 'a|lib/test.html': '''<html> | |
| 191 <polymer-element name="x-a"> | |
| 192 <script src="foo.js"></script> | |
| 193 </polymer-element> | |
| 194 </html>'''.replaceAll(' ', ''), | |
| 195 }, { | |
| 196 'a|lib/test.html.messages': '' | |
| 197 }); | |
| 198 | |
| 199 _testLinter('in polymer-element, inlined', { | |
| 200 'a|lib/test.html': '''<html> | |
| 201 <polymer-element name="x-a"> | |
| 202 <script>foo...</script> | |
| 203 </polymer-element> | |
| 204 </html>'''.replaceAll(' ', ''), | |
| 205 }, { | |
| 206 'a|lib/test.html.messages': | |
| 207 'warning: script tag in polymer element with no type will ' | |
| 208 'be treated as JavaScript. Did you forget type="application/dart"?' | |
| 209 ' (lib/test.html 2 0)' | |
| 210 }); | |
| 211 | |
| 212 _testLinter('top-level, dart type & .dart url', { | |
| 213 'a|lib/test.html': '''<html> | |
| 214 <script type="applicatino/dart" src="foo.dart"></script> | |
| 215 </html>'''.replaceAll(' ', ''), | |
| 216 }, { | |
| 217 'a|lib/test.html.messages': '' | |
| 218 }); | |
| 219 | |
| 220 _testLinter('top-level, dart type & .js url', { | |
| 221 'a|lib/test.html': '''<html> | |
| 222 <script type="application/dart" src="foo.js"></script> | |
| 223 </html>'''.replaceAll(' ', ''), | |
| 224 }, { | |
| 225 'a|lib/test.html.messages': | |
| 226 'warning: "application/dart" scripts should use the .dart file ' | |
| 227 'extension. (lib/test.html 1 0)' | |
| 228 }); | |
| 229 }); | |
| 230 | |
| 231 _testLinter('script tags should have only src url or inline code', { | |
| 232 'a|lib/test.html': '''<html> | |
| 233 <script type="application/dart" src="foo.dart">more</script> | |
| 234 </html>'''.replaceAll(' ', ''), | |
| 235 }, { | |
| 236 'a|lib/test.html.messages': | |
| 237 'warning: script tag has "src" attribute and also has script text. ' | |
| 238 '(lib/test.html 1 0)' | |
| 239 }); | |
| 240 | |
| 241 group('event handlers', () { | |
| 242 _testLinter('onfoo is not polymer', { | |
| 243 'a|lib/test.html': '''<html><body> | |
| 244 <div onfoo="something"></div> | |
| 245 '''.replaceAll(' ', ''), | |
| 246 }, { | |
| 247 'a|lib/test.html.messages': | |
| 248 'warning: Event handler "onfoo" will be interpreted as an inline ' | |
| 249 'JavaScript event handler. Use the form ' | |
| 250 'on-event-name="handlerName" if you want a Dart handler ' | |
| 251 'that will automatically update the UI based on model changes. ' | |
| 252 '(lib/test.html 1 0)' | |
| 253 }); | |
| 254 | |
| 255 _testLinter('on-foo is only supported in polymer elements', { | |
| 256 'a|lib/test.html': '''<html><body> | |
| 257 <div on-foo="something"></div> | |
| 258 '''.replaceAll(' ', ''), | |
| 259 }, { | |
| 260 'a|lib/test.html.messages': | |
| 261 'warning: Inline event handlers are only supported inside ' | |
| 262 'declarations of <polymer-element>. ' | |
| 263 '(lib/test.html 1 0)' | |
| 264 }); | |
| 265 | |
| 266 _testLinter('on-foo is not an expression', { | |
| 267 'a|lib/test.html': '''<html><body> | |
| 268 <polymer-element name="x-a"><div on-foo="bar()"></div> | |
| 269 </polymer-element> | |
| 270 '''.replaceAll(' ', ''), | |
| 271 }, { | |
| 272 'a|lib/test.html.messages': | |
| 273 'warning: Invalid event handler body "bar()". Declare a method ' | |
| 274 'in your custom element "void handlerName(event, detail, target)" ' | |
| 275 'and use the form on-foo="handlerName". ' | |
| 276 '(lib/test.html 1 28)' | |
| 277 }); | |
| 278 }); | |
| 279 | |
| 280 group('using custom tags', () { | |
| 281 _testLinter('tag exists (x-tag)', { | |
| 282 'a|lib/test.html': '<x-foo></x-foo>', | |
| 283 }, { | |
| 284 'a|lib/test.html.messages': | |
| 285 'warning: definition for custom element with tag name "x-foo" not ' | |
| 286 'found. (lib/test.html 0 0)' | |
| 287 }); | |
| 288 | |
| 289 _testLinter('tag exists (type extension)', { | |
| 290 'a|lib/test.html': '<div is="x-foo"></div>', | |
| 291 }, { | |
| 292 'a|lib/test.html.messages': | |
| 293 'warning: definition for custom element with tag name "x-foo" not ' | |
| 294 'found. (lib/test.html 0 0)' | |
| 295 }); | |
| 296 | |
| 297 _testLinter('used correctly (no base tag)', { | |
| 298 'a|lib/test.html': ''' | |
| 299 <polymer-element name="x-a"></polymer-element> | |
| 300 <x-a></x-a> | |
| 301 '''.replaceAll(' ', ''), | |
| 302 }, { | |
| 303 'a|lib/test.html.messages': '' | |
| 304 }); | |
| 305 | |
| 306 _testLinter('used incorrectly (no base tag)', { | |
| 307 'a|lib/test.html': ''' | |
| 308 <polymer-element name="x-a"></polymer-element> | |
| 309 <div is="x-a"></div> | |
| 310 '''.replaceAll(' ', ''), | |
| 311 }, { | |
| 312 'a|lib/test.html.messages': | |
| 313 'warning: custom element "x-a" doesn\'t declare any type ' | |
| 314 'extensions. To fix this, either rewrite this tag as ' | |
| 315 '<x-a> or add \'extends="div"\' to ' | |
| 316 'the custom element declaration. (lib/test.html 1 0)' | |
| 317 }); | |
| 318 | |
| 319 _testLinter('used incorrectly, imported def (no base tag)', { | |
| 320 'a|lib/b.html': '<polymer-element name="x-a"></polymer-element>', | |
| 321 'a|lib/test.html': ''' | |
| 322 <link rel="import" href="b.html"> | |
| 323 <div is="x-a"></div> | |
| 324 '''.replaceAll(' ', ''), | |
| 325 }, { | |
| 326 'a|lib/test.html.messages': | |
| 327 'warning: custom element "x-a" doesn\'t declare any type ' | |
| 328 'extensions. To fix this, either rewrite this tag as ' | |
| 329 '<x-a> or add \'extends="div"\' to ' | |
| 330 'the custom element declaration. (lib/test.html 1 0)' | |
| 331 }); | |
| 332 | |
| 333 _testLinter('used correctly (base tag)', { | |
| 334 'a|lib/b.html': ''' | |
| 335 <polymer-element name="x-a" extends="div"> | |
| 336 </polymer-element> | |
| 337 '''.replaceAll(' ', ''), | |
| 338 'a|lib/test.html': ''' | |
| 339 <link rel="import" href="b.html"> | |
| 340 <div is="x-a"></div> | |
| 341 '''.replaceAll(' ', ''), | |
| 342 }, { | |
| 343 'a|lib/test.html.messages': '' | |
| 344 }); | |
| 345 | |
| 346 _testLinter('used incorrectly (missing base tag)', { | |
| 347 'a|lib/b.html': ''' | |
| 348 <polymer-element name="x-a" extends="div"> | |
| 349 </polymer-element> | |
| 350 '''.replaceAll(' ', ''), | |
| 351 'a|lib/test.html': ''' | |
| 352 <link rel="import" href="b.html"> | |
| 353 <x-a></x-a> | |
| 354 '''.replaceAll(' ', ''), | |
| 355 }, { | |
| 356 'a|lib/test.html.messages': '' | |
| 357 'warning: custom element "x-a" extends from "div", but this tag ' | |
| 358 'will not include the default properties of "div". To fix this, ' | |
| 359 'either write this tag as <div is="x-a"> or remove the "extends" ' | |
| 360 'attribute from the custom element declaration. (lib/test.html 1 0)' | |
| 361 }); | |
| 362 | |
| 363 _testLinter('used incorrectly (wrong base tag)', { | |
| 364 'a|lib/b.html': ''' | |
| 365 <polymer-element name="x-a" extends="div"> | |
| 366 </polymer-element> | |
| 367 '''.replaceAll(' ', ''), | |
| 368 'a|lib/test.html': ''' | |
| 369 <link rel="import" href="b.html"> | |
| 370 <span is="x-a"></span> | |
| 371 '''.replaceAll(' ', ''), | |
| 372 }, { | |
| 373 'a|lib/test.html.messages': '' | |
| 374 'warning: custom element "x-a" extends from "div". Did you mean ' | |
| 375 'to write <div is="x-a">? (lib/test.html 1 0)' | |
| 376 }); | |
| 377 | |
| 378 _testLinter('used incorrectly (wrong base tag, transitive)', { | |
| 379 'a|lib/c.html': ''' | |
| 380 <polymer-element name="x-c" extends="li"> | |
| 381 </polymer-element> | |
| 382 <polymer-element name="x-b" extends="x-c"> | |
| 383 </polymer-element> | |
| 384 '''.replaceAll(' ', ''), | |
| 385 'a|lib/b.html': ''' | |
| 386 <link rel="import" href="c.html"> | |
| 387 <polymer-element name="x-a" extends="x-b"> | |
| 388 </polymer-element> | |
| 389 '''.replaceAll(' ', ''), | |
| 390 'a|lib/test.html': ''' | |
| 391 <link rel="import" href="b.html"> | |
| 392 <span is="x-a"></span> | |
| 393 '''.replaceAll(' ', ''), | |
| 394 }, { | |
| 395 'a|lib/test.html.messages': '' | |
| 396 'warning: custom element "x-a" extends from "li". Did you mean ' | |
| 397 'to write <li is="x-a">? (lib/test.html 1 0)' | |
| 398 }); | |
| 399 }); | |
| 400 } | |
| 401 | |
| 402 _testLinter(String name, Map inputFiles, Map outputMessages) { | |
| 403 var linter = new Linter(_testFormatter); | |
| 404 var outputFiles = {}; | |
| 405 inputFiles.forEach((k, v) => outputFiles[k] = v); | |
| 406 outputMessages.forEach((k, v) => outputFiles[k] = v); | |
| 407 var keys = inputFiles.keys.toSet(); | |
| 408 keys.retainAll(outputMessages.keys); | |
| 409 expect(keys, isEmpty); | |
| 410 testPhases(name, [[linter]], inputFiles, outputFiles); | |
| 411 } | |
| 412 | |
| 413 | |
| 414 _testFormatter(String kind, String message, Span span) { | |
| 415 var formattedMessage = '$kind: $message'; | |
| 416 if (span != null) { | |
| 417 formattedMessage = '$formattedMessage ' | |
| 418 '(${span.sourceUrl} ${span.start.line} ${span.start.column})'; | |
| 419 } | |
| 420 return formattedMessage; | |
| 421 } | |
| OLD | NEW |