Commit fb25a43f authored by Dan Beam's avatar Dan Beam Committed by Commit Bot

web_dev_style: add a presubmit for .bind(this) as it's discouraged by

both Chromium and Google's JS style guides.

R=rbpotter@chromium.org
BUG=997405

Change-Id: I07ffb05cc2992e36f2d7674cf7660491f8f2e222
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1768950
Commit-Queue: Dan Beam <dbeam@chromium.org>
Auto-Submit: Dan Beam <dbeam@chromium.org>
Reviewed-by: default avatarRebekah Potter <rbpotter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#690211}
parent 6fe44208
...@@ -20,31 +20,35 @@ class JSChecker(object): ...@@ -20,31 +20,35 @@ class JSChecker(object):
return regex_check.RegexCheck( return regex_check.RegexCheck(
self.input_api.re, line_number, line, regex, message) self.input_api.re, line_number, line, regex, message)
def BindThisCheck(self, i, line):
return self.RegexCheck(i, line, r"(\.bind\(this)[^)]*\)",
"Prefer arrow (=>) functions over bind(this)");
def ChromeSendCheck(self, i, line): def ChromeSendCheck(self, i, line):
"""Checks for a particular misuse of 'chrome.send'.""" """Checks for a particular misuse of "chrome.send"."""
return self.RegexCheck(i, line, r"chrome\.send\('[^']+'\s*(, \[\])\)", return self.RegexCheck(i, line, r"chrome\.send\('[^']+'\s*(, \[\])\)",
'Passing an empty array to chrome.send is unnecessary') "Passing an empty array to chrome.send is unnecessary")
def CommentIfAndIncludeCheck(self, line_number, line): def CommentIfAndIncludeCheck(self, line_number, line):
return self.RegexCheck(line_number, line, r'(?<!\/\/ )(<if|<include) ', return self.RegexCheck(line_number, line, r"(?<!\/\/ )(<if|<include) ",
'<if> or <include> should be in a single line comment with a space ' + "<if> or <include> should be in a single line comment with a space " +
'after the slashes. Examples:\n' + "after the slashes. Examples:\n" +
' // <include src="...">\n' + ' // <include src="...">\n' +
' // <if expr="chromeos">\n' + ' // <if expr="chromeos">\n' +
' // </if>\n') " // </if>\n")
def EndJsDocCommentCheck(self, i, line): def EndJsDocCommentCheck(self, i, line):
msg = 'End JSDoc comments with */ instead of **/' msg = "End JSDoc comments with */ instead of **/"
def _check(regex): def _check(regex):
return self.RegexCheck(i, line, regex, msg) return self.RegexCheck(i, line, regex, msg)
return _check(r'^\s*(\*\*/)\s*$') or _check(r'/\*\* @[a-zA-Z]+.* (\*\*/)') return _check(r"^\s*(\*\*/)\s*$") or _check(r"/\*\* @[a-zA-Z]+.* (\*\*/)")
def ExtraDotInGenericCheck(self, i, line): def ExtraDotInGenericCheck(self, i, line):
return self.RegexCheck(i, line, r"((?:Array|Object|Promise)\.<)", return self.RegexCheck(i, line, r"((?:Array|Object|Promise)\.<)",
"Don't use a dot after generics (Object.<T> should be Object<T>).") "Don't use a dot after generics (Object.<T> should be Object<T>).")
def InheritDocCheck(self, i, line): def InheritDocCheck(self, i, line):
"""Checks for use of '@inheritDoc' instead of '@override'.""" """Checks for use of "@inheritDoc" instead of "@override"."""
return self.RegexCheck(i, line, r"\* (@inheritDoc)", return self.RegexCheck(i, line, r"\* (@inheritDoc)",
"@inheritDoc is deprecated, use @override instead") "@inheritDoc is deprecated, use @override instead")
...@@ -53,7 +57,7 @@ class JSChecker(object): ...@@ -53,7 +57,7 @@ class JSChecker(object):
return self.RegexCheck(i, line, r"(?<!this)(\.\$)[\[\.](?![a-zA-Z]+\()", return self.RegexCheck(i, line, r"(?<!this)(\.\$)[\[\.](?![a-zA-Z]+\()",
"Please only use this.$.localId, not element.$.localId") "Please only use this.$.localId, not element.$.localId")
def RunEsLintChecks(self, affected_js_files, format='stylish'): def RunEsLintChecks(self, affected_js_files, format="stylish"):
"""Runs lint checks using ESLint. The ESLint rules being applied are defined """Runs lint checks using ESLint. The ESLint rules being applied are defined
in the .eslintrc.js configuration file. in the .eslintrc.js configuration file.
""" """
...@@ -64,7 +68,7 @@ class JSChecker(object): ...@@ -64,7 +68,7 @@ class JSChecker(object):
for f in affected_js_files: for f in affected_js_files:
affected_js_files_paths.append(f.AbsoluteLocalPath()) affected_js_files_paths.append(f.AbsoluteLocalPath())
args = ['--color', '--format', format, '--ignore-pattern \'!.eslintrc.js\''] args = ["--color", "--format", format, "--ignore-pattern '!.eslintrc.js'"]
args += affected_js_files_paths args += affected_js_files_paths
import eslint import eslint
...@@ -79,10 +83,10 @@ class JSChecker(object): ...@@ -79,10 +83,10 @@ class JSChecker(object):
"Please use variable namesLikeThis <https://goo.gl/eQiXVW>") "Please use variable namesLikeThis <https://goo.gl/eQiXVW>")
def _GetErrorHighlight(self, start, length): def _GetErrorHighlight(self, start, length):
"""Takes a start position and a length, and produces a row of '^'s to """Takes a start position and a length, and produces a row of "^"s to
highlight the corresponding part of a string. highlight the corresponding part of a string.
""" """
return start * ' ' + length * '^' return start * " " + length * "^"
def RunChecks(self): def RunChecks(self):
"""Check for violations of the Chromium JavaScript style guide. See """Check for violations of the Chromium JavaScript style guide. See
...@@ -92,7 +96,7 @@ class JSChecker(object): ...@@ -92,7 +96,7 @@ class JSChecker(object):
affected_files = self.input_api.AffectedFiles(file_filter=self.file_filter, affected_files = self.input_api.AffectedFiles(file_filter=self.file_filter,
include_deletes=False) include_deletes=False)
affected_js_files = filter(lambda f: f.LocalPath().endswith('.js'), affected_js_files = filter(lambda f: f.LocalPath().endswith(".js"),
affected_files) affected_files)
if affected_js_files: if affected_js_files:
...@@ -114,13 +118,13 @@ class JSChecker(object): ...@@ -114,13 +118,13 @@ class JSChecker(object):
if error_lines: if error_lines:
error_lines = [ error_lines = [
'Found JavaScript style violations in %s:' % "Found JavaScript style violations in %s:" %
f.LocalPath()] + error_lines f.LocalPath()] + error_lines
results.append(self.output_api.PresubmitError('\n'.join(error_lines))) results.append(self.output_api.PresubmitError("\n".join(error_lines)))
if results: if results:
results.append(self.output_api.PresubmitNotifyResult( results.append(self.output_api.PresubmitNotifyResult(
'See the JavaScript style guide at ' "See the JavaScript style guide at "
'https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/web.md#JavaScript')) "https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/web.md#JavaScript"))
return results return results
...@@ -11,12 +11,12 @@ import test_util ...@@ -11,12 +11,12 @@ import test_util
import unittest import unittest
_HERE = os_path.dirname(os_path.abspath(__file__)) _HERE = os_path.dirname(os_path.abspath(__file__))
sys_path.append(os_path.join(_HERE, '..', '..')) sys_path.append(os_path.join(_HERE, "..", ".."))
from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
_DECLARATION_METHODS = 'const', 'let', 'var' _DECLARATION_METHODS = "const", "let", "var"
class JsCheckerTest(unittest.TestCase): class JsCheckerTest(unittest.TestCase):
...@@ -25,17 +25,46 @@ class JsCheckerTest(unittest.TestCase): ...@@ -25,17 +25,46 @@ class JsCheckerTest(unittest.TestCase):
self.checker = js_checker.JSChecker(MockInputApi(), MockOutputApi()) self.checker = js_checker.JSChecker(MockInputApi(), MockOutputApi())
def ShouldFailBindThisCheck(self, line):
error = self.checker.BindThisCheck(1, line)
self.assertNotEqual("", error, "Should be flagged as style error: " + line)
highlight = test_util.GetHighlight(line, error).strip()
self.assertTrue(highlight.startswith(".bind(this"))
def ShouldPassBindThisCheck(self, line):
self.assertEqual("", self.checker.BindThisCheck(1, line),
"Should not be flagged as style error: " + line)
def testBindThisFails(self):
lines = [
'let bound = this.method_.bind(this);',
"cr.doc.addEventListener('click', this.onClick_.bind(this));",
'this.api_.onEvent = this.onClick_.bind(this);',
'this.api_.getThinger(this.gotThinger_.bind(this));',
'this.api_.getThinger(this.gotThinger_.bind(this, param1, param2));',
]
for line in lines:
self.ShouldFailBindThisCheck(line)
def testBindThisPasses(self):
lines = [
'// And in the darkness bind them.',
'this.methodName_.bind(scope, param)',
]
for line in lines:
self.ShouldPassBindThisCheck(line)
def ShouldFailCommentCheck(self, line): def ShouldFailCommentCheck(self, line):
"""Checks that uncommented '<if>' and '<include>' are a style error.""" """Checks that uncommented "<if>" and "<include>" are a style error."""
error = self.checker.CommentIfAndIncludeCheck(1, line) error = self.checker.CommentIfAndIncludeCheck(1, line)
self.assertNotEqual('', error, 'Should be flagged as style error: ' + line) self.assertNotEqual("", error, "Should be flagged as style error: " + line)
highlight = test_util.GetHighlight(line, error).strip() highlight = test_util.GetHighlight(line, error).strip()
self.assertTrue(highlight.startswith(('<if', '<include'))) self.assertTrue(highlight.startswith(("<if", "<include")))
def ShouldPassCommentCheck(self, line): def ShouldPassCommentCheck(self, line):
"""Checks that commented '<if>' and '<include>' are allowed.""" """Checks that commented "<if>" and "<include>" are allowed."""
self.assertEqual('', self.checker.CommentIfAndIncludeCheck(1, line), self.assertEqual("", self.checker.CommentIfAndIncludeCheck(1, line),
'Should not be flagged as style error: ' + line) "Should not be flagged as style error: " + line)
def testCommentFails(self): def testCommentFails(self):
lines = [ lines = [
...@@ -64,18 +93,17 @@ class JsCheckerTest(unittest.TestCase): ...@@ -64,18 +93,17 @@ class JsCheckerTest(unittest.TestCase):
self.ShouldPassCommentCheck(line) self.ShouldPassCommentCheck(line)
def ShouldFailChromeSendCheck(self, line): def ShouldFailChromeSendCheck(self, line):
"""Checks that the 'chrome.send' checker flags |line| as a style error.""" """Checks that the "chrome.send" checker flags |line| as a style error."""
error = self.checker.ChromeSendCheck(1, line) error = self.checker.ChromeSendCheck(1, line)
self.assertNotEqual('', error, self.assertNotEqual("", error, "Should be flagged as style error: " + line)
'Should be flagged as style error: ' + line) self.assertEqual(test_util.GetHighlight(line, error), ", []")
self.assertEqual(test_util.GetHighlight(line, error), ', []')
def ShouldPassChromeSendCheck(self, line): def ShouldPassChromeSendCheck(self, line):
"""Checks that the 'chrome.send' checker doesn't flag |line| as a style """Checks that the "chrome.send" checker doesn"t flag |line| as a style
error. error.
""" """
self.assertEqual('', self.checker.ChromeSendCheck(1, line), self.assertEqual("", self.checker.ChromeSendCheck(1, line),
'Should not be flagged as style error: ' + line) "Should not be flagged as style error: " + line)
def testChromeSendFails(self): def testChromeSendFails(self):
lines = [ lines = [
...@@ -87,10 +115,10 @@ class JsCheckerTest(unittest.TestCase): ...@@ -87,10 +115,10 @@ class JsCheckerTest(unittest.TestCase):
def testChromeSendPasses(self): def testChromeSendPasses(self):
lines = [ lines = [
"chrome.send('message', constructArgs('foo', []));", 'chrome.send("message", constructArgs("foo", []));',
" chrome.send('message', constructArgs('foo', []));", ' chrome.send("message", constructArgs("foo", []));',
"chrome.send('message', constructArgs([]));", 'chrome.send("message", constructArgs([]));',
" chrome.send('message', constructArgs([]));", ' chrome.send("message", constructArgs([]));',
] ]
for line in lines: for line in lines:
self.ShouldPassChromeSendCheck(line) self.ShouldPassChromeSendCheck(line)
...@@ -98,14 +126,13 @@ class JsCheckerTest(unittest.TestCase): ...@@ -98,14 +126,13 @@ class JsCheckerTest(unittest.TestCase):
def ShouldFailEndJsDocCommentCheck(self, line): def ShouldFailEndJsDocCommentCheck(self, line):
"""Checks that the **/ checker flags |line| as a style error.""" """Checks that the **/ checker flags |line| as a style error."""
error = self.checker.EndJsDocCommentCheck(1, line) error = self.checker.EndJsDocCommentCheck(1, line)
self.assertNotEqual('', error, self.assertNotEqual("", error, "Should be flagged as style error: " + line)
'Should be flagged as style error: ' + line) self.assertEqual(test_util.GetHighlight(line, error), "**/")
self.assertEqual(test_util.GetHighlight(line, error), '**/')
def ShouldPassEndJsDocCommentCheck(self, line): def ShouldPassEndJsDocCommentCheck(self, line):
"""Checks that the **/ checker doesn't flag |line| as a style error.""" """Checks that the **/ checker doesn"t flag |line| as a style error."""
self.assertEqual('', self.checker.EndJsDocCommentCheck(1, line), self.assertEqual("", self.checker.EndJsDocCommentCheck(1, line),
'Should not be flagged as style error: ' + line) "Should not be flagged as style error: " + line)
def testEndJsDocCommentFails(self): def testEndJsDocCommentFails(self):
lines = [ lines = [
...@@ -131,7 +158,7 @@ class JsCheckerTest(unittest.TestCase): ...@@ -131,7 +158,7 @@ class JsCheckerTest(unittest.TestCase):
def ShouldFailExtraDotInGenericCheck(self, line): def ShouldFailExtraDotInGenericCheck(self, line):
"""Checks that Array.< or Object.< is flagged as a style nit.""" """Checks that Array.< or Object.< is flagged as a style nit."""
error = self.checker.ExtraDotInGenericCheck(1, line) error = self.checker.ExtraDotInGenericCheck(1, line)
self.assertNotEqual('', error) self.assertNotEqual("", error)
self.assertTrue(test_util.GetHighlight(line, error).endswith(".<")) self.assertTrue(test_util.GetHighlight(line, error).endswith(".<"))
def testExtraDotInGenericFails(self): def testExtraDotInGenericFails(self):
...@@ -144,18 +171,17 @@ class JsCheckerTest(unittest.TestCase): ...@@ -144,18 +171,17 @@ class JsCheckerTest(unittest.TestCase):
self.ShouldFailExtraDotInGenericCheck(line) self.ShouldFailExtraDotInGenericCheck(line)
def ShouldFailInheritDocCheck(self, line): def ShouldFailInheritDocCheck(self, line):
"""Checks that the '@inheritDoc' checker flags |line| as a style error.""" """Checks that the "@inheritDoc" checker flags |line| as a style error."""
error = self.checker.InheritDocCheck(1, line) error = self.checker.InheritDocCheck(1, line)
self.assertNotEqual('', error, self.assertNotEqual("", error, "Should be flagged as style error: " + line)
msg='Should be flagged as style error: ' + line) self.assertEqual(test_util.GetHighlight(line, error), "@inheritDoc")
self.assertEqual(test_util.GetHighlight(line, error), '@inheritDoc')
def ShouldPassInheritDocCheck(self, line): def ShouldPassInheritDocCheck(self, line):
"""Checks that the '@inheritDoc' checker doesn't flag |line| as a style """Checks that the "@inheritDoc" checker doesn"t flag |line| as a style
error. error.
""" """
self.assertEqual('', self.checker.InheritDocCheck(1, line), self.assertEqual("", self.checker.InheritDocCheck(1, line),
msg='Should not be flagged as style error: ' + line) msg="Should not be flagged as style error: " + line)
def testInheritDocFails(self): def testInheritDocFails(self):
lines = [ lines = [
...@@ -178,22 +204,21 @@ class JsCheckerTest(unittest.TestCase): ...@@ -178,22 +204,21 @@ class JsCheckerTest(unittest.TestCase):
def ShouldFailPolymerLocalIdCheck(self, line): def ShouldFailPolymerLocalIdCheck(self, line):
"""Checks that element.$.localId check marks |line| as a style error.""" """Checks that element.$.localId check marks |line| as a style error."""
error = self.checker.PolymerLocalIdCheck(1, line) error = self.checker.PolymerLocalIdCheck(1, line)
self.assertNotEqual('', error, self.assertNotEqual("", error, "Should be flagged as style error: " + line)
msg='Should be flagged as a style error: ' + line) self.assertTrue(".$" in test_util.GetHighlight(line, error))
self.assertTrue('.$' in test_util.GetHighlight(line, error))
def ShouldPassPolymerLocalIdCheck(self, line): def ShouldPassPolymerLocalIdCheck(self, line):
"""Checks that element.$.localId check doesn't mark |line| as a style """Checks that element.$.localId check doesn't mark |line| as a style
error.""" error."""
self.assertEqual('', self.checker.PolymerLocalIdCheck(1, line), self.assertEqual("", self.checker.PolymerLocalIdCheck(1, line),
msg='Should not be flagged as a style error: ' + line) msg="Should not be flagged as a style error: " + line)
def testPolymerLocalIdFails(self): def testPolymerLocalIdFails(self):
lines = [ lines = [
"cat.$.dog", "cat.$.dog",
"thing1.$.thing2", "thing1.$.thing2",
"element.$.localId", "element.$.localId",
"element.$['fancy-hyphenated-id']", 'element.$["fancy-hyphenated-id"]',
] ]
for line in lines: for line in lines:
self.ShouldFailPolymerLocalIdCheck(line) self.ShouldFailPolymerLocalIdCheck(line)
...@@ -202,7 +227,7 @@ class JsCheckerTest(unittest.TestCase): ...@@ -202,7 +227,7 @@ class JsCheckerTest(unittest.TestCase):
lines = [ lines = [
"this.$.id", "this.$.id",
"this.$.localId", "this.$.localId",
"this.$['fancy-id']", 'this.$["fancy-id"]',
"this.page.$.flushForTesting()", "this.page.$.flushForTesting()",
] ]
for line in lines: for line in lines:
...@@ -211,15 +236,14 @@ class JsCheckerTest(unittest.TestCase): ...@@ -211,15 +236,14 @@ class JsCheckerTest(unittest.TestCase):
def ShouldFailVariableNameCheck(self, line): def ShouldFailVariableNameCheck(self, line):
"""Checks that var unix_hacker, $dollar are style errors.""" """Checks that var unix_hacker, $dollar are style errors."""
error = self.checker.VariableNameCheck(1, line) error = self.checker.VariableNameCheck(1, line)
self.assertNotEqual('', error, self.assertNotEqual("", error, "Should be flagged as style error: " + line)
msg='Should be flagged as style error: ' + line)
highlight = test_util.GetHighlight(line, error) highlight = test_util.GetHighlight(line, error)
self.assertFalse(any(dm in highlight for dm in _DECLARATION_METHODS)) self.assertFalse(any(dm in highlight for dm in _DECLARATION_METHODS))
def ShouldPassVariableNameCheck(self, line): def ShouldPassVariableNameCheck(self, line):
"""Checks that variableNamesLikeThis aren't style errors.""" """Checks that variableNamesLikeThis aren"t style errors."""
self.assertEqual('', self.checker.VariableNameCheck(1, line), self.assertEqual("", self.checker.VariableNameCheck(1, line),
msg='Should not be flagged as style error: ' + line) msg="Should not be flagged as style error: " + line)
def testVariableNameFails(self): def testVariableNameFails(self):
lines = [ lines = [
...@@ -252,5 +276,5 @@ class JsCheckerTest(unittest.TestCase): ...@@ -252,5 +276,5 @@ class JsCheckerTest(unittest.TestCase):
self.ShouldPassVariableNameCheck(line % declaration_method) self.ShouldPassVariableNameCheck(line % declaration_method)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment