2 # Copyright 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
7 from os
import path
as os_path
9 from sys
import path
as sys_path
12 _HERE
= os_path
.dirname(os_path
.abspath(__file__
))
13 sys_path
.append(os_path
.join(_HERE
, '..', '..', '..', 'tools'))
15 import find_depot_tools
# pylint: disable=W0611
16 from testing_support
.super_mox
import SuperMoxTestBase
19 class CssCheckerTest(SuperMoxTestBase
):
21 SuperMoxTestBase
.setUp(self
)
23 self
.fake_file_name
= 'fake.css'
25 self
.fake_file
= self
.mox
.CreateMockAnything()
26 self
.mox
.StubOutWithMock(self
.fake_file
, 'LocalPath')
27 self
.fake_file
.LocalPath().AndReturn(self
.fake_file_name
)
28 # Actual calls to NewContents() are defined in each test.
29 self
.mox
.StubOutWithMock(self
.fake_file
, 'NewContents')
31 self
.input_api
= self
.mox
.CreateMockAnything()
32 self
.input_api
.re
= re
33 self
.mox
.StubOutWithMock(self
.input_api
, 'AffectedSourceFiles')
34 self
.input_api
.AffectedFiles(
35 include_deletes
=False, file_filter
=None).AndReturn([self
.fake_file
])
37 # Actual creations of PresubmitPromptWarning are defined in each test.
38 self
.output_api
= self
.mox
.CreateMockAnything()
39 self
.mox
.StubOutWithMock(self
.output_api
, 'PresubmitPromptWarning',
40 use_mock_anything
=True)
42 self
.output_api
= self
.mox
.CreateMockAnything()
43 self
.mox
.StubOutWithMock(self
.output_api
, 'PresubmitNotifyResult',
44 use_mock_anything
=True)
46 def VerifyContentsIsValid(self
, contents
):
47 self
.fake_file
.NewContents().AndReturn(contents
.splitlines())
49 css_checker
.CSSChecker(self
.input_api
, self
.output_api
).RunChecks()
51 def VerifyContentsProducesOutput(self
, contents
, output
):
52 self
.fake_file
.NewContents().AndReturn(contents
.splitlines())
53 author_msg
= ('Was the CSS checker useful? '
54 'Send feedback or hate mail to dbeam@chromium.org.')
55 self
.output_api
.PresubmitNotifyResult(author_msg
).AndReturn(None)
56 self
.output_api
.PresubmitPromptWarning(
57 self
.fake_file_name
+ ':\n' + output
.strip()).AndReturn(None)
59 css_checker
.CSSChecker(self
.input_api
, self
.output_api
).RunChecks()
61 def testCssAlphaWithAtBlock(self
):
62 self
.VerifyContentsProducesOutput("""
63 <include src="../shared/css/cr/ui/overlay.css">
64 <include src="chrome://resources/totally-cool.css" />
66 /* A hopefully safely ignored comment and @media statement. /**/
76 <if expr="not is macosx">
77 background-image: url(chrome://resources/BLAH); /* TODO(dbeam): Fix this. */
78 background-color: rgb(235, 239, 249);
81 background-color: white;
82 background-image: url(chrome://resources/BLAH2);
88 .language-options-right {
90 opacity: 1; /* TODO(dbeam): Fix this. */
93 - Alphabetize properties and list vendor specific (i.e. -webkit) above standard.
100 def testCssStringWithAt(self
):
101 self
.VerifyContentsIsValid("""
103 background-image: url(images/google_logo.png@2x);
106 body.alternate-logo #logo {
107 -webkit-mask-image: url(images/google_logo.png@2x);
118 def testCssAlphaWithNonStandard(self
):
119 self
.VerifyContentsProducesOutput("""
121 /* A hopefully safely ignored comment and @media statement. /**/
123 -webkit-margin-start: 5px;
125 - Alphabetize properties and list vendor specific (i.e. -webkit) above standard.
127 -webkit-margin-start: 5px;""")
129 def testCssAlphaWithLongerDashedProps(self
):
130 self
.VerifyContentsProducesOutput("""
132 border-left: 5px; /* A hopefully removed comment. */
133 border: 5px solid red;
135 - Alphabetize properties and list vendor specific (i.e. -webkit) above standard.
137 border: 5px solid red;""")
139 def testCssBracesHaveSpaceBeforeAndNothingAfter(self
):
140 self
.VerifyContentsProducesOutput("""
141 /* Hello! */div/* Comment here*/{
153 }; /* This should be ignored. */
156 .this.is { /* allowed */
159 - Start braces ({) end a selector, have a space before them and no rules after.
163 def testCssClassesUseDashes(self
):
164 self
.VerifyContentsProducesOutput("""
167 .class-name /* We should not catch this. */,
171 - Classes use .dash-form.
176 def testCssCloseBraceOnNewLine(self
):
177 self
.VerifyContentsProducesOutput("""
178 @media { /* TODO(dbeam) Fix this case. */
183 @-webkit-keyframe blah {
184 from { height: rotate(-10turn); }
185 100% { height: 500px; }
188 #id { /* ${TemplateExpressions} should be ignored. */
202 rule: value; }""", """
203 - Always put a rule closing brace (}) on a new line.
206 def testCssColonsHaveSpaceAfter(self
):
207 self
.VerifyContentsProducesOutput("""
208 div:not(.class):not([attr=5]), /* We should not catch this. */
209 div:not(.class):not([attr]) /* Nor this. */ {
210 background: url(data:image/jpeg,asdfasdfsadf); /* Ignore this. */
211 background: -webkit-linear-gradient(left, red,
216 - Colons (:) should have a space after them.
219 - Don't use data URIs in source files. Use grit instead.
220 background: url(data:image/jpeg,asdfasdfsadf);""")
222 def testCssFavorSingleQuotes(self
):
223 self
.VerifyContentsProducesOutput("""
224 html[dir="rtl"] body,
225 html[dir=ltr] body /* TODO(dbeam): Require '' around rtl in future? */ {
226 font-family: "Open Sans";
227 <if expr="is_macosx">
231 - Use single quotes (') instead of double quotes (") in strings.
232 html[dir="rtl"] body,
233 font-family: "Open Sans";""")
235 def testCssHexCouldBeShorter(self
):
236 self
.VerifyContentsProducesOutput("""
244 background-color: #336699; /* Ignore short hex rule if not gray. */
248 - Use abbreviated hex (#rgb) when in form #rrggbb.
249 color: #999999; (replace with #999)
251 - Use rgb() over #hex when not a shade of gray (like #333).
252 background-color: #336699; (replace with rgb(51, 102, 153))""")
254 def testCssUseMillisecondsForSmallTimes(self
):
255 self
.VerifyContentsProducesOutput("""
256 .transition-0s /* This is gross but may happen. */ {
260 transform: four 300ms;
262 - Use milliseconds for time measurements under 1 second.
263 transform: one 0.2s; (replace with 200ms)
264 transform: two .1s; (replace with 100ms)""")
266 def testCssNoDataUrisInSourceFiles(self
):
267 self
.VerifyContentsProducesOutput("""
269 background: url( data:image/jpeg,4\/\/350|\/|3|2 );
271 - Don't use data URIs in source files. Use grit instead.
272 background: url( data:image/jpeg,4\/\/350|\/|3|2 );""")
274 def testCssNoQuotesInUrl(self
):
275 self
.VerifyContentsProducesOutput("""
277 background: url('chrome://resources/images/blah.jpg');
278 background: url("../../folder/hello.png");
280 - Use single quotes (') instead of double quotes (") in strings.
281 background: url("../../folder/hello.png");
283 - Don't use quotes in url().
284 background: url('chrome://resources/images/blah.jpg');
285 background: url("../../folder/hello.png");""")
287 def testCssOneRulePerLine(self
):
288 self
.VerifyContentsProducesOutput("""
289 a:not([hidden]):not(.custom-appearance):not([version=1]):first-of-type,
290 a:not([hidden]):not(.custom-appearance):not([version=1]):first-of-type ~
291 input[type='checkbox']:not([hidden]),
293 background: url(chrome://resources/BLAH);
294 rule: value; /* rule: value; */
295 rule: value; rule: value;
304 - One rule per line (what not to do: color: red; margin: 0;).
305 rule: value; rule: value;""")
307 def testCssOneSelectorPerLine(self
):
308 self
.VerifyContentsProducesOutput("""
311 div,/* Hello! */ span,
312 #id.class([dir=rtl):not(.class):any(a, b, d) {
318 some-other: rule here;
320 - One selector per line (what not to do: a, b {}).
325 def testCssPseudoElementDoubleColon(self
):
326 self
.VerifyContentsProducesOutput("""
329 ::-webkit-scrollbar-thumb,
330 a:not([empty]):hover:focus:active, /* shouldn't catch here and above */
332 .tree-label:empty:after,
337 - Pseudo-elements should use double colon (i.e. ::after).
338 :after (should be ::after)
339 :after (should be ::after)
340 :before (should be ::before)
341 :-WebKit-ScrollBar (should be ::-WebKit-ScrollBar)
344 def testCssRgbIfNotGray(self
):
345 self
.VerifyContentsProducesOutput("""
349 background: -webkit-linear-gradient(left, from(#abc), to(#def));
353 - Use rgb() over #hex when not a shade of gray (like #333).
354 background: -webkit-linear-gradient(left, from(#abc), to(#def)); """
355 """(replace with rgb(170, 187, 204), rgb(221, 238, 255))
356 color: #bad; (replace with rgb(187, 170, 221))
357 color: #bada55; (replace with rgb(186, 218, 85))""")
359 def testWebkitBeforeOrAfter(self
):
360 self
.VerifyContentsProducesOutput("""
362 -webkit-margin-before: 10px;
363 -webkit-margin-start: 20px;
364 -webkit-padding-after: 3px;
365 -webkit-padding-end: 5px;
368 - Use *-top/bottom instead of -webkit-*-before/after.
369 -webkit-margin-before: 10px; (replace with margin-top)
370 -webkit-padding-after: 3px; (replace with padding-bottom)""")
372 def testCssZeroWidthLengths(self
):
373 self
.VerifyContentsProducesOutput("""
374 @-webkit-keyframe anim {
375 0% { /* Ignore key frames */
387 background-image: url(images/google_logo.png@2x);
390 body.alternate-logo #logo {
391 -webkit-mask-image: url(images/google_logo.png@2x);
394 /* http://crbug.com/359682 */
395 #spinner-container #spinner {
396 -webkit-animation-duration: 1.0s;
399 .media-button.play > .state0.active,
400 .media-button[state='0'] > .state0.normal /* blah */, /* blee */
401 .media-button[state='0']:not(.disabled):hover > .state0.hover {
402 -webkit-animation: anim 0s;
403 -webkit-animation-duration: anim 0ms;
404 -webkit-transform: scale(0%);
405 background-position-x: 0em;
406 background-position-y: 0ex;
408 color: hsl(0, 0%, 85%); /* Shouldn't trigger error. */
419 - Use "0" for zero-width lengths (i.e. 0px -> 0)
421 -webkit-transform: scale(0%);
422 background-position-x: 0em;
423 background-position-y: 0ex;
434 if __name__
== '__main__':