From 867680f4e9deaf1212a4b303738e03430a48ae6e Mon Sep 17 00:00:00 2001 From: Nick Hehr Date: Wed, 20 May 2015 10:31:34 -0400 Subject: [PATCH 01/19] Updates LaunchTest() form text input to be of type "url". Making this change not only adds automatic url validation from modern browsers but enabled special keyboards on mobile devices to make inputing urls much easier. --- front/src/views/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/views/index.html b/front/src/views/index.html index aff58e1..13b0504 100644 --- a/front/src/views/index.html +++ b/front/src/views/index.html @@ -2,7 +2,7 @@

Free and open source!

- +
@@ -90,4 +90,4 @@

JS Profiling

Untangles the JavaScript spaghetti code

-
\ No newline at end of file + From 49f93e660e5080d10db62dfc0afa42dff9d8d892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Fri, 22 May 2015 10:05:41 +0200 Subject: [PATCH 02/19] User agent sniffing --- front/src/js/app.js | 1 + front/src/views/index.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/front/src/js/app.js b/front/src/js/app.js index 6a229fb..4fcfa1a 100644 --- a/front/src/js/app.js +++ b/front/src/js/app.js @@ -19,6 +19,7 @@ var yltApp = angular.module('YellowLabTools', [ ]); yltApp.run(['$rootScope', '$location', function($rootScope, $location) { + $rootScope.isTouchDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(window.navigator.userAgent); $rootScope.loadedRunId = null; // Google Analytics diff --git a/front/src/views/index.html b/front/src/views/index.html index 13b0504..f83be65 100644 --- a/front/src/views/index.html +++ b/front/src/views/index.html @@ -2,7 +2,7 @@

Free and open source!

- +
From c93ce684a81fd27cd259681c25a79e543b14c4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Fri, 22 May 2015 10:52:12 +0200 Subject: [PATCH 03/19] Old jQuery major versions didn't have the trailing .0 in number --- lib/metadata/policies.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index a203b1c..8862497 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -389,17 +389,17 @@ var policies = { score = 70; } else if (value.indexOf('1.8.') === 0) { score = 50; - } else if (value.indexOf('1.7.') === 0) { + } else if (value.indexOf('1.7') === 0) { score = 40; - } else if (value.indexOf('1.6.') === 0) { + } else if (value.indexOf('1.6') === 0) { score = 30; - } else if (value.indexOf('1.5.') === 0) { + } else if (value.indexOf('1.5') === 0) { score = 20; - } else if (value.indexOf('1.4.') === 0) { + } else if (value.indexOf('1.4') === 0) { score = 10; - } else if (value.indexOf('1.3.') === 0) { + } else if (value.indexOf('1.3') === 0) { score = 0; - } else if (value.indexOf('1.2.') === 0) { + } else if (value.indexOf('1.2') === 0) { score = 0; } else { debug('Unknown jQuery version "%s"', value); @@ -433,7 +433,7 @@ var policies = { "jQueryFunctionsUsed": { "tool": "jsExecutionTransformer", "label": "jQuery usage", - "message": "

This is the number of different jQuery functions called on load. This rule is not trying to blame you for using jQuery too much, but the opposite.

If only a few functions are used, why not trying to get rid of jQuery? Have a look at http://youmightnotneedjquery.com.

", + "message": "

This is the number of different core jQuery functions called on load. This rule is not trying to blame you for using jQuery too much, but the opposite.

If only a few functions are used, why not trying to get rid of jQuery? Have a look at http://youmightnotneedjquery.com.

", "isOkThreshold": 15, "isBadThreshold": 6, "isAbnormalThreshold": 0, From 03a25df3670bf426cf221a47c58cc33000cf903c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Fri, 22 May 2015 10:57:39 +0200 Subject: [PATCH 04/19] One webfont is ok --- lib/metadata/policies.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index 8862497..5285096 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -1024,7 +1024,7 @@ var policies = { "tool": "phantomas", "label": "Font count", "message": "

Fonts are loaded on the critical path of the head. Load as few as possible.

", - "isOkThreshold": 0, + "isOkThreshold": 1, "isBadThreshold": 3, "isAbnormalThreshold": 5, "hasOffenders": true, From 719e624ddf1eb3de46dbdce69efa2a1f5340915c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Fri, 22 May 2015 11:09:29 +0200 Subject: [PATCH 05/19] Fix icons showing a background grid on IE --- front/src/css/icons.css | 2 +- front/src/css/main.css | 2 +- front/src/fonts/svg-icons/arrow-left3.svg | 2 +- front/src/fonts/svg-icons/bars.svg | 2 +- front/src/fonts/svg-icons/lab.svg | 2 +- front/src/fonts/svg-icons/list.svg | 2 +- front/src/fonts/svg-icons/loop.svg | 2 +- front/src/fonts/svg-icons/mobile.svg | 2 +- front/src/fonts/svg-icons/question.svg | 2 +- front/src/fonts/svg-icons/screen.svg | 2 +- front/src/fonts/svg-icons/tablet.svg | 2 +- front/src/fonts/svg-icons/warning.svg | 4 +--- front/src/less/icons.less | 2 +- 13 files changed, 13 insertions(+), 15 deletions(-) diff --git a/front/src/css/icons.css b/front/src/css/icons.css index fab4f27..8a7c99f 100644 --- a/front/src/css/icons.css +++ b/front/src/css/icons.css @@ -1,6 +1,6 @@ @font-face { font-family: "icons"; - src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); + src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); font-weight: normal; font-style: normal; } diff --git a/front/src/css/main.css b/front/src/css/main.css index fed5912..0e3b998 100644 --- a/front/src/css/main.css +++ b/front/src/css/main.css @@ -1,6 +1,6 @@ @font-face { font-family: "icons"; - src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); + src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAA7wAA4AAAAAGUAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABRAAAAEMAAABWQLtM2mNtYXAAAAGIAAAAOgAAAUriHBC2Y3Z0IAAAAcQAAAAUAAAAIv+YA5pmcGdtAAAB2AAABacAAAuX2BTb8Gdhc3AAAAeAAAAACAAAAAgAAAAQZ2x5ZgAAB4gAAATFAAAHKo/0IxloZWFkAAAMUAAAADMAAAA2BCiQNmhoZWEAAAyEAAAAIAAAACQD/QHAaG10eAAADKQAAAAPAAAALBYAAABsb2NhAAAMtAAAABgAAAAYCMwKe21heHAAAAzMAAAAIAAAACABDAvxbmFtZQAADOwAAAEdAAAB8hTbwvhwb3N0AAAODAAAAGIAAAB/4g/aQ3ByZXAAAA5wAAAAfQAAAIqMht7EeJxjYGRiYJzAwMrAwejCmMbAwOAOpb8ySDK0MDAwMbAyM2AFAWmuKQwOHxk/cjEe+H+AQY/xIIMDUJgRJAcAixQLGgB4nGNgYGBmgGAZBkYGEHAB8hjBfBYGDSDNBqQZGZgYGD5y/f8PUvCREUTzM0DVAwEjG8OIBwB5MwbBAAB4nGNgwAA8EMh48P8BEAYAFnAFM3icrVb5c9NGFJZ8JE5CjpKDFvVYsXGa2iuTUggGTAiSZRfcw7laCUorxU56H9Ayw9/gv+bJtDP0N/60fm9lm0CSdoZpJqP37e6nffeTyVCCjL3AD4VoPTNmtlo0tnMvoMsWrYbRoejtBZQpxn8XjILR6ch9y7bJCMnwZL1vmIYXuQ6ZikR06FBGia6g523Krdzrr5qTnt/xt+8HtrStXiCo3Q5s2gwtQVVG1TAUSUqKu7SKrcFK0BqfrzHzeTsQMKIXC5psBxF2BJ9NMlpntB5ZURiGFpnlMJRktIODMHQoqwTuyRVjGJT32gHlpUtj0oX5IZmRQzklYZfoJvl9V/BJqpyfOPc7lC3Z2PdET/Rwd7KWL8KtrSBqW/F2GMgQp5s7AY4sdmqg2aG8onGv3DcyaWjGsJSuRIilG1Nm/5DMDu6nfMmhcSXYyCmv8yxn7Au+gTajkClRXRtZUP3xKcPz3ZI9CvaEejn4k+ktZhkmePA4En5PxpwIHSnD4miSsGDk0ErKFmVcT1VMnfI6LeMtw3rh2tGXzijtUH9qMusHtiXtsGQ7NK2STManblx3aEaBKASd8e7y6wDSDWmaV9tYTWPl0CyumdMhEYhAB3ppxotELxI0g6A5NKdau0GS69bDZZo+kE8cekO1toLWTrpp2dif1/tnVWLMentBMjuL/MUuzZa5SFG6bnKGH9N4kLmETGSL7SDh4MFbt4f0stqSLfHaEFvpOb+C2uedEJ40YX8Tuy+n6pQEJoYxLxEtj4yNvmmaOlfzykiMjL8b0Kx0hU9TKMpJKI5cEUH9X3NzpjFjuG4vSs6Olelx2bqAMC3At/myQ4sqMVkuIc4sz6kky/JNleRYvqWSPMvzKhljaalknOXbKimwfEclEyw/UKJC5gOHSho8dKiswSOH3lUGTZdfw8b3YOO7uFvARpY2bGR5ATaylLCR5TJsZFmEjSxXYCPL92Ejy1XYyFIpUdOl5iionYuExyZ4Oh1oH8X1VlHklMlBJ11EETfFKZmQcVXyGPtXBkrJobVReswlulhK8uaiH2AMsYMfHo3M8eNLSlzR9n4EnukfV4IOO1E57xtLfxr8V9+Q1eSSuQiPLsN/GHyyvSjsuOrQFVU5V3No/b+oKMIO6FeREmOpKCqiyc2LUN7p9ZqyiW4PMNYxFtHR66a5uAD9VUyZJTQI/jWFJrzyQa8ihaj1cNe1F8eikt5BOdwJlqCI+31zK3iaEVlhPc2sZM+HLs/AAqap1GzZQPd5r7ZSxHMoHfYZL+pKynpxF8cZL7aAI55Br74TwyQMZtlADiU0NOAXhNaC+05QItNpl0ODI/Z5FFT+2K24kT0qaiPwbKdT7oUupPw6x0BgJ78yiIGsITQ39DYV0DxCNGSTlXG2ajpk7MAgosZuUBE1fBvZ4sGmYFuGIR8rYnXn6Nc3TdRJFTzIjOQyvjmwwBumJuLP86suDlO5oaSocNQaGMy1sJJUzAU04K3Rdvvo9ubL7BM5txVVyyde6iq6Vu5BMRcLrD3OQVoqVAHVG1XYMLpcXBKlXkGTpNfVMTQww1+jFJv/V/Wx+TxfahIj5Ei+7XBgo8/BGPrfYP9tOQjAwI+Ry024vJg2J77u6MP5Cl1GL358yv4dzFxzYZ6uAN9VdBWixVHzEVfRwKdsGKdPFJcjtQA/VX3MGYDPAEwGn6u+qXfaAHpnizk+wDZzGOwwh8EucxjsMec2wBfMYfAlcxgEzGEQMscDuMccBveZw+Ar5jB4wJwGwNfMYfANcxhEzGEQM8cF2GcOgw5zGHSZw+BA0fVRmA95QRtA32p0C+g7XU9YbGLxvaIbI/YPvNDsHzVi9k8aMfVnRbUR9RdeaOqvGjH1N42Y+lDRzRH1ES809XeNmPqHRkx9rJ5O5DLDH09umQoHlF1uPxl+U5x/ACtxTWMAAAEAAf//AA94nHWUz27bRhDGZ3bJXeofJVIUKUuyLEqJHUSGHUiWlSKBQxQN6khKG6BJ61QtKiNAcwhiqEaaa9lj0UP9BIEfwIcCSXtmgb5FgEKXPkR6oDtLWY6boAS5I5G75My3v/mAA5xGDDCCDHiwCXeDAWhc55o+BSmYkGwKOgipi0fAOYwNBMgNU8gYjgHRxFEui9C+unrJX6lWilbWy3lShwxm0sJpYx09E+UGru1gcQf79IP+1nGtv91xnTx2aRRt9GhsbuLqFsLTI86Pns7HveeMPd9LxlthulJMDV6atm2+HKSKlXSYwuh85tOjuLuYSuPfoZpxx67Zd9QyWks1wOmM6gyBg6Ra81CEG8F1MDTjEDShHQIVewgsJXUqk/F9oOrgAVB4CAh4t2hbZ0chLZbbPb/k97ul1uIqdq0WgxiCIJgszkYQTWazMMQgjuhFwJTW+Ia09mAZekGntlRATQccCNQpL51PNUaTEmVzQ9Lb5KNyubxcXna2Wt2mFEvty7zX7WxvrbaxKRz3Bpa4cD1/By/3e92Sh3+9KMhUSh6rofCieqmHQe9SHD4OHm+gfJIyU3Q+id9s7iDu/Hjy7NmJykucM/BWGw8+Dz4D5BpHbUoCcU0cgC4NqRtTMNLSSB9QPRyVUgIID7kPacik0hlSjggB9aWR55acC8JlSThLCUbiyXdiGEZKp+idiFE4CSZn10LDJNcC+PBpMMpJwRmu1MuOxjU2KCDseqh/DAw1ZCp1KoAfqM0cJ4kJ5Fwfg66b+si2ahW3ZPm2X7xM6npt3MAWAeoRqh3Xk90eNlfX+q1Sv45dukfw4m+tr3v3f1i+6X5z68rt6m50CkkYnt/G71tXcDzwrn+E9cpuFAdqHC7uKb21hINjqkEpXYMG3A4+VNoLLpWauib0R0AknHebpi26reQwaKzUl5fKTq1UI20LaQl5lk+6TeVuUcupXC3acMVlc3VL9Zro+5aP05lTRaw68/BnGAbUUDXbPjxVjEf0JJ7MZ+CxU/UK8a56WrMnGCzyPuMkT1mvw5fBF9mMwbmGA+AaaQ1TsgaKTFMYoADcT6Ouy3EGpSQqhDDGYBimMWr6VkHXrqz66831pXKhYTXMXDql5fW84sRtJ03VcZdRtnr9Hl1Nkzl11tlhWxts7YKvzIIgNG0C5HhydXhzZeXm8P48XL3gJZMAG7Y5oZZsBPfeTlHh2gUXmfOlfOI46YMPgm3VHxLEPkhDZ8gk7hNXnLMHFBh/CJzxuwB5M5elFZmE85TqU8tXpJd8Sp2T9MobZhOEyQzDOGwkAx0YnAIkHkzqKx7KxIP6qkYv1vgj4hTGIqFAXvBcq8CgVq0slZxC2SrPCTAUAf4ZAf6cAP6f3T+Ko7PNDZL4SqomTf0Uz7Dx3ubH1twtrpFlqr0//Yf2XuAfpJDK0oeff3eLlCIOhr869/aCDQOp08gwlFugcguho9APlPPq30pMHI30/YrANtmoGmz+/wJyw4P3VjwMylZB442VWnVeNoGX1/KkdqmN3Ocdr+gK6W9gv7jtddySbK1tbUvHVW7ZJAlPcO/Eyr3O2pX4k4qdfZ2zTDu9nrYtu2LTid/Fv2DkxiOvjlj38JUbETAT045y6rH9L7s+GfsAAAB4nGNgZGBgAOIZ7P+q4vltvjJwMzGAwMWWdU8g9IIr/3/9P8DExngQyOVgAEsDAHIyDgIAeJxjYGRgYDzw/wCDHhMDA8P/L0w8DEARFMANAHttBL94nGNiYGBgIhIDAAI8ABcAAAAAAAB0ALYBAgFYAboCFgKIAsIDFAOVAAEAAAALADAABgAAAAAAAgAYACgAdwAAAGsLlwAAAAB4nF2OvU7DMBSFT/qHaBACITGbpQtS+jP2AdqZDtnTxElbJXHkuJUqMTPzFMw8Bc/FiXslKmzp+jvnHl8bwAN+EKBbAYa+dquHG6oL90l3wgPyo/AQIZ6FR1QvwmO8YiIc4glvnBAMbumMkQn3cI9auE//XXhA/hAecvqn8Ij+l/AYMb6FQ0yC0T41dbvRxbFMrGdfYm3bvanVPJp5vda1tonTmdqeVXsqFs7lKremUitTO12WRjXWHHTqop1zzXI6zcWPUlNhjxSGf26xgUaBI0oksFf+H8VMWO90WmGOCLOr/pr92mcSOJ4ZM1ucWVucOHtB1yGnzpkxqEgrf7dLl9yGTuN7Bzop/Qg7f6vBElPu/F8+8q9XvzD1U2IAAAB4nG3BQQ7CMAwEQG+bpi15Cyde5CBTRTIxOEb9PgeuzNBEPwv9V4gwYUbCgowVG3ZcUAq723lVecQtVfYxK9ekbURSs1d+Wm0q2/sjI5r1PO4u0nNwVYn1ZO+tH0RflcgY/gAAeJxj8N7BcCIoYiMjY1/kBsadHAwcDMkFGxnYnDYxMDJogRibuZkYOSAsfgYwi91pFzMDUJoTyOZw2sXgAGEzM7hsVGHsCIzY4NARsZE5xWWjGoi3i6OBgZHFoSM5JAKkJBIINvMyMfJo7WD837qBpXcjE1Afa4oLAGWtJJsAAAA=") format("woff"); font-weight: normal; font-style: normal; } diff --git a/front/src/fonts/svg-icons/arrow-left3.svg b/front/src/fonts/svg-icons/arrow-left3.svg index 71c9b9d..edd6f1c 100644 --- a/front/src/fonts/svg-icons/arrow-left3.svg +++ b/front/src/fonts/svg-icons/arrow-left3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/fonts/svg-icons/bars.svg b/front/src/fonts/svg-icons/bars.svg index f491fb9..03dac05 100644 --- a/front/src/fonts/svg-icons/bars.svg +++ b/front/src/fonts/svg-icons/bars.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/fonts/svg-icons/lab.svg b/front/src/fonts/svg-icons/lab.svg index ab42c55..0cf1263 100644 --- a/front/src/fonts/svg-icons/lab.svg +++ b/front/src/fonts/svg-icons/lab.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/fonts/svg-icons/list.svg b/front/src/fonts/svg-icons/list.svg index da0a00b..98a4cd3 100644 --- a/front/src/fonts/svg-icons/list.svg +++ b/front/src/fonts/svg-icons/list.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/fonts/svg-icons/loop.svg b/front/src/fonts/svg-icons/loop.svg index 17a0bba..84d3184 100755 --- a/front/src/fonts/svg-icons/loop.svg +++ b/front/src/fonts/svg-icons/loop.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/fonts/svg-icons/mobile.svg b/front/src/fonts/svg-icons/mobile.svg index 5136a81..5cb8089 100755 --- a/front/src/fonts/svg-icons/mobile.svg +++ b/front/src/fonts/svg-icons/mobile.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/fonts/svg-icons/question.svg b/front/src/fonts/svg-icons/question.svg index 7f447ee..c78d38d 100644 --- a/front/src/fonts/svg-icons/question.svg +++ b/front/src/fonts/svg-icons/question.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/fonts/svg-icons/screen.svg b/front/src/fonts/svg-icons/screen.svg index eb2048c..c0cdff9 100755 --- a/front/src/fonts/svg-icons/screen.svg +++ b/front/src/fonts/svg-icons/screen.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/fonts/svg-icons/tablet.svg b/front/src/fonts/svg-icons/tablet.svg index 45f83ab..b701c87 100755 --- a/front/src/fonts/svg-icons/tablet.svg +++ b/front/src/fonts/svg-icons/tablet.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/fonts/svg-icons/warning.svg b/front/src/fonts/svg-icons/warning.svg index c0b4811..3fd4284 100644 --- a/front/src/fonts/svg-icons/warning.svg +++ b/front/src/fonts/svg-icons/warning.svg @@ -1,3 +1 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/front/src/less/icons.less b/front/src/less/icons.less index 40b7108..b1eb7fa 100644 --- a/front/src/less/icons.less +++ b/front/src/less/icons.less @@ -4,7 +4,7 @@ @font-face { font-family:"icons"; - src:url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); + src:url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); font-weight:normal; font-style:normal; } From 701530ef04f42d59e1ecc8b27666cc82e7de2e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Fri, 22 May 2015 11:16:18 +0200 Subject: [PATCH 06/19] Be cooler on global variables --- lib/metadata/policies.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index 5285096..cb1caec 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -351,9 +351,9 @@ var policies = { "tool": "phantomas", "label": "Global variables", "message": "

It is a bad practice because they clutter up the global namespace. If two scripts use the same variable name in the global scope, it can cause conflicts and it is generally hard to debug.

Global variables also take a (very) little bit longer to be accessed than variables in the local scope of a function.

", - "isOkThreshold": 10, - "isBadThreshold": 50, - "isAbnormalThreshold": 200, + "isOkThreshold": 30, + "isBadThreshold": 100, + "isAbnormalThreshold": 400, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { From 1ff983a03b172730334522b707ed87d710fecd68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Fri, 22 May 2015 17:44:54 +0200 Subject: [PATCH 07/19] Add specific sub-filters on warnings in the timeline --- front/src/css/timeline.css | 26 +++++++++++++-- front/src/js/controllers/timelineCtrl.js | 8 +++++ .../src/js/directives/offendersDirectives.js | 15 ++++++++- front/src/less/timeline.less | 33 +++++++++++++++++-- front/src/views/timeline.html | 25 ++++++++++++-- lib/tools/jsExecutionTransformer.js | 6 ++-- 6 files changed, 102 insertions(+), 11 deletions(-) diff --git a/front/src/css/timeline.css b/front/src/css/timeline.css index 2816533..9ecf796 100644 --- a/front/src/css/timeline.css +++ b/front/src/css/timeline.css @@ -143,6 +143,10 @@ border: 1px dotted #aaa; text-align: left; } +.subFilters { + margin-left: 3em; + font-size: 0.9em; +} .table { display: table; width: 100%; @@ -306,9 +310,27 @@ color: #e74c3c; cursor: pointer; } -.warningsFilterOn > div { +.queryWithoutResultsFilterOn > div { display: none; } -.warningsFilterOn > div.warning { +.queryWithoutResultsFilterOn > div.queryWithoutResults { + display: table-row; +} +.jQueryCallOnEmptyObjectFilterOn > div { + display: none; +} +.jQueryCallOnEmptyObjectFilterOn > div.jQueryCallOnEmptyObject { + display: table-row; +} +.eventNotDelegatedFilterOn > div { + display: none; +} +.eventNotDelegatedFilterOn > div.eventNotDelegated { + display: table-row; +} +.jsErrorFilterOn > div { + display: none; +} +.jsErrorFilterOn > div.jsError { display: table-row; } diff --git a/front/src/js/controllers/timelineCtrl.js b/front/src/js/controllers/timelineCtrl.js index 3887ff1..0dac9eb 100644 --- a/front/src/js/controllers/timelineCtrl.js +++ b/front/src/js/controllers/timelineCtrl.js @@ -4,6 +4,14 @@ timelineCtrl.controller('TimelineCtrl', ['$scope', '$rootScope', '$routeParams', $scope.runId = $routeParams.runId; $scope.Menu = Menu.setCurrentPage('timeline', $scope.runId); + $scope.warningsFilterOn = false; + $scope.warningsFilters = { + queryWithoutResults: true, + jQueryCallOnEmptyObject: true, + eventNotDelegated: true, + jsError: true + }; + function loadResults() { // Load result if needed if (!$rootScope.loadedResult || $rootScope.loadedResult.runId !== $routeParams.runId) { diff --git a/front/src/js/directives/offendersDirectives.js b/front/src/js/directives/offendersDirectives.js index df2b35c..0eb0693 100644 --- a/front/src/js/directives/offendersDirectives.js +++ b/front/src/js/directives/offendersDirectives.js @@ -766,10 +766,23 @@ element.append(getProfilerLineHTML(scope.index, scope.node)); element[0].id = 'line_' + scope.index; - if (scope.node.warning || scope.node.error) { + if (scope.node.warning) { element[0].classList.add('warning'); + + if (scope.node.queryWithoutResults) { + element[0].classList.add('queryWithoutResults'); + } + + if (scope.node.jQueryCallOnEmptyObject) { + element[0].classList.add('jQueryCallOnEmptyObject'); + } + + if (scope.node.eventNotDelegated) { + element[0].classList.add('eventNotDelegated'); + } } + // Bind click on the details icon var detailsIcon = element[0].querySelector('.details div'); if (detailsIcon) { diff --git a/front/src/less/timeline.less b/front/src/less/timeline.less index 1968752..ab9bd80 100644 --- a/front/src/less/timeline.less +++ b/front/src/less/timeline.less @@ -162,6 +162,11 @@ text-align: left; } +.subFilters { + margin-left: 3em; + font-size: 0.9em; +} + .table { display: table; width: 100%; @@ -346,10 +351,34 @@ color: #e74c3c; cursor: pointer; } -.warningsFilterOn { +.queryWithoutResultsFilterOn { > div { display: none; - &.warning { + &.queryWithoutResults { + display: table-row; + } + } +} +.jQueryCallOnEmptyObjectFilterOn { + > div { + display: none; + &.jQueryCallOnEmptyObject { + display: table-row; + } + } +} +.eventNotDelegatedFilterOn { + > div { + display: none; + &.eventNotDelegated { + display: table-row; + } + } +} +.jsErrorFilterOn { + > div { + display: none; + &.jsError { display: table-row; } } diff --git a/front/src/views/timeline.html b/front/src/views/timeline.html index 497340e..e0bc1e0 100644 --- a/front/src/views/timeline.html +++ b/front/src/views/timeline.html @@ -60,11 +60,30 @@

- - + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ {{queryWithoutResultsFilterOn}}
-
+
Type
diff --git a/lib/tools/jsExecutionTransformer.js b/lib/tools/jsExecutionTransformer.js index 486cf4b..9beff21 100644 --- a/lib/tools/jsExecutionTransformer.js +++ b/lib/tools/jsExecutionTransformer.js @@ -15,9 +15,7 @@ var jsExecutionTransformer = function() { queriesWithoutResults: 0 }; - var offenders = { - - }; + var offenders = {}; var hasjQuery = (data.toolsResults.phantomas.metrics.jQueryVersionsLoaded > 0); if (hasjQuery) { @@ -53,11 +51,13 @@ var jsExecutionTransformer = function() { if (node.data.resultsNumber === 0) { metrics.queriesWithoutResults ++; + node.queryWithoutResults = true; node.warning = true; } if (contextLength === 0) { metrics.jQueryCallsOnEmptyObject ++; + node.jQueryCallOnEmptyObject = true; node.warning = true; } From 2fb4a7bd6ad0d446d1c4a0b8d9bdaa9f89e21c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Fri, 22 May 2015 17:47:13 +0200 Subject: [PATCH 08/19] Replace rules DOMinserts, DOMqueries & eventsBound by DOMaccesses --- front/src/views/rule.html | 16 +++--- lib/metadata/policies.js | 75 ++------------------------- lib/metadata/scoreProfileGeneric.json | 8 ++- 3 files changed, 13 insertions(+), 86 deletions(-) diff --git a/front/src/views/rule.html b/front/src/views/rule.html index 3e72f34..7f1b693 100644 --- a/front/src/views/rule.html +++ b/front/src/views/rule.html @@ -30,10 +30,6 @@
{{offender.id}}: {{offender.occurrences}} occurrences
- -
- appended to -
{{offender.query}} (in ) using {{offender.fn}} @@ -43,10 +39,6 @@ {{offender.query}} (in ) using {{offender.fn}}: {{offender.count}} queries
-
- {{offender.eventName}} bound to -
-
Scroll event bound on window Scroll event bound on document @@ -218,10 +210,14 @@
{{offender.color}} ({{offender.occurrences}} times)
-
-
+ +
+

Offenders

+ Please open the JS timeline +
+

404

Rule "{{policyName}}"" not found diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index cb1caec..a0a2b19 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -66,52 +66,14 @@ var policies = { }; } }, - /*"DOMaccesses": { + "DOMaccesses": { "tool": "jsExecutionTransformer", "label": "DOM access", - "message": "

TODO

TODO

", + "message": "

This metric counts the number of calls to DOM related functions (both native DOM functions and jQuery functions) on page load.

The more your JavaScript code accesses the DOM, the slower the page will load.

Try, as much as possible, to have an HTML page fully generated by the server instead of making changes with JS.

Try to reduce the number of queries by refactoring your JavaScript code.

Binding too many events also has a cost. Try to use event delegation as much as possible.

", "isOkThreshold": 50, - "isBadThreshold": 2000, + "isBadThreshold": 1500, "isAbnormalThreshold": 3000, - "hasOffenders": false - },*/ - "DOMinserts": { - "tool": "phantomas", - "label": "DOM inserts", - "message": "

Working with the DOM in JavaScript triggers layout calculations and slows down the page.

Try, as much as possible, to have an HTML page fully generated by the server instead of making changes with JS.

", - "isOkThreshold": 10, - "isBadThreshold": 400, - "isAbnormalThreshold": 1000, - "hasOffenders": true, - "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var parts = /^"(.*)" ?appended ?to ?"(.*)"$/.exec(offender); - - if (!parts) { - debug('DOMinserts offenders transform function error with "%s"', offender); - return { - parseError: offender - }; - } - - return { - insertedElement: offendersHelpers.domPathToDomElementObj(parts[1]), - receiverElement: offendersHelpers.domPathToDomElementObj(parts[2]) - }; - }) - }; - } - }, - "DOMqueries": { - "tool": "phantomas", - "label": "DOM queries", - "message": "

DOM queries are like looking in a large catalog of items. Even if the browsers made progress on the performances of queries, websites often make hundreds of them.

Try to reduce the number of queries by refactoring your JavaScript code.

Avoid also to have a read query between two write queries. To be able to reduce the number repaints and optimize performances, browsers buffer the DOM writing operations and treat them in bulk. But each time a DOM reading is asked, the browser needs to empty the buffer. This can be particularly slow inside a loop.

", - "isOkThreshold": 50, - "isBadThreshold": 1000, - "isAbnormalThreshold": 2000, - "hasOffenders": false + "hasOffenders": true }, "DOMqueriesWithoutResults": { "tool": "phantomas", @@ -175,35 +137,6 @@ var policies = { }; } }, - "eventsBound": { - "tool": "phantomas", - "label": "Events bound", - "message": "

Binding too many events has a cost.

It can be avoided by using \"event delegation\". Instead of binding events on each element one by one, events delegation binds them on the top level document element and uses the bubbling principle. It will imperceptibly slow down the event when it occurs, but the loading of the page will speed-up.

", - "isOkThreshold": 100, - "isBadThreshold": 800, - "isAbnormalThreshold": 1500, - "hasOffenders": true, - "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var parts = /^"(.*)" ?bound ?to ?"(.*)"$/.exec(offender); - - if (!parts) { - debug('eventsBound offenders transform function error with "%s"', offender); - return { - parseError: offender - }; - } - - return { - eventName: parts[1], - element: offendersHelpers.domPathToDomElementObj(parts[2]) - }; - }) - }; - } - }, "eventsScrollBound": { "tool": "phantomas", "label": "Scroll events bound", diff --git a/lib/metadata/scoreProfileGeneric.json b/lib/metadata/scoreProfileGeneric.json index aa2590c..b4be55e 100644 --- a/lib/metadata/scoreProfileGeneric.json +++ b/lib/metadata/scoreProfileGeneric.json @@ -12,11 +12,9 @@ "domManipulations": { "label": "DOM manipulations", "policies": { - "DOMinserts": 2, - "DOMqueries": 1, - "DOMqueriesWithoutResults": 2, - "DOMqueriesAvoidable": 2, - "eventsBound": 1 + "DOMaccesses": 3, + "DOMqueriesWithoutResults": 1, + "DOMqueriesAvoidable": 1 } }, "scroll": { From 33621af3ad2c28b2d0f5dd03f3428c76701ac2d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Fri, 22 May 2015 18:54:32 +0200 Subject: [PATCH 09/19] Replaced DOMqueriesWithoutResults by queriesWithoutResults to include jQuery queries --- front/src/js/app.js | 11 ++++++++ front/src/js/controllers/timelineCtrl.js | 36 ++++++++++++++++++------ front/src/views/rule.html | 11 ++++---- lib/metadata/policies.js | 33 ++++------------------ lib/metadata/scoreProfileGeneric.json | 2 +- 5 files changed, 52 insertions(+), 41 deletions(-) diff --git a/front/src/js/app.js b/front/src/js/app.js index 4fcfa1a..f981a02 100644 --- a/front/src/js/app.js +++ b/front/src/js/app.js @@ -22,6 +22,17 @@ yltApp.run(['$rootScope', '$location', function($rootScope, $location) { $rootScope.isTouchDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(window.navigator.userAgent); $rootScope.loadedRunId = null; + var oldHash; + + // We don't want the hash to be kept between two pages + $rootScope.$on('$locationChangeStart', function(param1, param2, param3, param4){ + var newHash = $location.hash(); + if (newHash === oldHash) { + $location.hash(null); + } + oldHash = newHash; + }); + // Google Analytics $rootScope.$on('$routeChangeSuccess', function(){ ga('send', 'pageview', {'page': $location.path()}); diff --git a/front/src/js/controllers/timelineCtrl.js b/front/src/js/controllers/timelineCtrl.js index 0dac9eb..509774e 100644 --- a/front/src/js/controllers/timelineCtrl.js +++ b/front/src/js/controllers/timelineCtrl.js @@ -4,14 +4,6 @@ timelineCtrl.controller('TimelineCtrl', ['$scope', '$rootScope', '$routeParams', $scope.runId = $routeParams.runId; $scope.Menu = Menu.setCurrentPage('timeline', $scope.runId); - $scope.warningsFilterOn = false; - $scope.warningsFilters = { - queryWithoutResults: true, - jQueryCallOnEmptyObject: true, - eventNotDelegated: true, - jsError: true - }; - function loadResults() { // Load result if needed if (!$rootScope.loadedResult || $rootScope.loadedResult.runId !== $routeParams.runId) { @@ -27,12 +19,40 @@ timelineCtrl.controller('TimelineCtrl', ['$scope', '$rootScope', '$routeParams', } function render() { + initFilters(); initScriptFiltering(); initExecutionTree(); initTimeline(); $timeout(initProfiler, 100); } + function initFilters() { + var hash = $location.hash(); + var filter = null; + + if (hash.indexOf('filter=') === 0) { + filter = hash.substr(7); + } + + $scope.warningsFilterOn = (filter !== null); + $scope.warningsFilters = { + queryWithoutResults: (filter === null || filter === 'queryWithoutResults'), + jQueryCallOnEmptyObject: (filter === null || filter === 'jQueryCallOnEmptyObject'), + eventNotDelegated: (filter === null || filter === 'eventNotDelegated'), + jsError: (filter === null || filter === 'jsError') + }; + + // If a filter is defined in the hash (ie: "/result/xxxxx/timeline#filter=queryWithoutResults") + var hash = $location.hash(); + if (hash.indexOf('filter=') === 0) { + var filter = hash.substr(7); + $scope.warningsFilterOn = true; + for (var key in $scope.warningsFilter) { + + } + } + } + function initScriptFiltering() { var offenders = $scope.result.rules.jsCount.offendersObj.list; $scope.scripts = []; diff --git a/front/src/views/rule.html b/front/src/views/rule.html index 7f1b693..5a4c884 100644 --- a/front/src/views/rule.html +++ b/front/src/views/rule.html @@ -30,10 +30,6 @@
{{offender.id}}: {{offender.occurrences}} occurrences
- -
- {{offender.query}} (in ) using {{offender.fn}} -
{{offender.query}} (in ) using {{offender.fn}}: {{offender.count}} queries @@ -214,10 +210,15 @@
-

Offenders

+

{{rule.value}} offenders

Please open the JS timeline
+
+

{{rule.value}} offenders

+ Please open the JS timeline, filtered by "Queries without results" +
+

404

Rule "{{policyName}}"" not found diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index a0a2b19..8c44614 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -73,37 +73,16 @@ var policies = { "isOkThreshold": 50, "isBadThreshold": 1500, "isAbnormalThreshold": 3000, - "hasOffenders": true + "hasOffenders": false }, - "DOMqueriesWithoutResults": { - "tool": "phantomas", - "label": "DOM queries without result", - "message": "

Number of queries that return no result.

It suggests the query is not used on the page, probably because it is some dead code.

Or maybe the code is trying to find an HTML block that is not always here. Look at the JS Timeline to see if the scripts correctly figures out the HTML block is not here and immediatly stops interacting further with the DOM.

", + "queriesWithoutResults": { + "tool": "jsExecutionTransformer", + "label": "Queries without result", + "message": "

Number of queries that return no result. Both native and jQuery DOM requests are counted.

It suggests the query is not used on the page, probably because it is some dead code.

Or maybe the code is trying to find an HTML block that is not always here. Look at the JS Timeline to see if the scripts correctly figures out the HTML block is not here and immediatly stops interacting further with the DOM.

", "isOkThreshold": 0, "isBadThreshold": 100, "isAbnormalThreshold": 200, - "hasOffenders": true, - "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var parts = /^(.*) ?\(in ?(.*)\) ?using ?(.*)$/.exec(offender); - - if (!parts) { - debug('DOMqueriesWithoutResults offenders transform function error with "%s"', offender); - return { - parseError: offender - }; - } - - return { - query: parts[1], - context: offendersHelpers.domPathToDomElementObj(parts[2]), - fn: parts[3] - }; - }) - }; - } + "hasOffenders": false }, "DOMqueriesAvoidable": { "tool": "phantomas", diff --git a/lib/metadata/scoreProfileGeneric.json b/lib/metadata/scoreProfileGeneric.json index b4be55e..77af6ee 100644 --- a/lib/metadata/scoreProfileGeneric.json +++ b/lib/metadata/scoreProfileGeneric.json @@ -13,7 +13,7 @@ "label": "DOM manipulations", "policies": { "DOMaccesses": 3, - "DOMqueriesWithoutResults": 1, + "queriesWithoutResults": 1, "DOMqueriesAvoidable": 1 } }, From 7f67c032d17723fb31155124fe9d52e437590832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 08:30:02 +0200 Subject: [PATCH 10/19] fix tests --- front/src/js/controllers/timelineCtrl.js | 10 -- test/core/customPoliciesTest.js | 182 +---------------------- 2 files changed, 1 insertion(+), 191 deletions(-) diff --git a/front/src/js/controllers/timelineCtrl.js b/front/src/js/controllers/timelineCtrl.js index 509774e..c641dc6 100644 --- a/front/src/js/controllers/timelineCtrl.js +++ b/front/src/js/controllers/timelineCtrl.js @@ -41,16 +41,6 @@ timelineCtrl.controller('TimelineCtrl', ['$scope', '$rootScope', '$routeParams', eventNotDelegated: (filter === null || filter === 'eventNotDelegated'), jsError: (filter === null || filter === 'jsError') }; - - // If a filter is defined in the hash (ie: "/result/xxxxx/timeline#filter=queryWithoutResults") - var hash = $location.hash(); - if (hash.indexOf('filter=') === 0) { - var filter = hash.substr(7); - $scope.warningsFilterOn = true; - for (var key in $scope.warningsFilter) { - - } - } } function initScriptFiltering() { diff --git a/test/core/customPoliciesTest.js b/test/core/customPoliciesTest.js index abfb601..784bf64 100644 --- a/test/core/customPoliciesTest.js +++ b/test/core/customPoliciesTest.js @@ -1,7 +1,7 @@ var should = require('chai').should(); var rulesChecker = require('../../lib/rulesChecker'); -describe('rulesChecker', function() { +describe('customPolicies', function() { var policies = require('../../lib/metadata/policies.js'); var results; @@ -70,131 +70,6 @@ describe('rulesChecker', function() { }); }); - - it('should transform DOMinserts offenders', function() { - results = rulesChecker.check({ - "toolsResults": { - "phantomas": { - "metrics": { - "DOMinserts": 4 - }, - "offenders": { - "DOMinserts": [ - "\"div\" appended to \"html\"", - "\"DocumentFragment > link[0]\" appended to \"head\"", - "\"div#Netaff-yh1XbS0vK3NaRGu\" appended to \"body > div#Global\"", - "\"img\" appended to \"body\"" - ] - } - } - } - }, policies); - - results.should.have.a.property('DOMinserts'); - results.DOMinserts.should.have.a.property('offendersObj').that.deep.equals({ - "count": 4, - "list": [ - { - "insertedElement": { - "type": "createdElement", - "element": "div" - }, - "receiverElement": { - "type": "html" - } - }, - { - "insertedElement": { - "type": "fragmentElement", - "element": "link[0]", - "tree": { - "DocumentFragment": { - "link[0]": 1 - } - } - }, - "receiverElement": { - "type": "head" - } - }, - { - "insertedElement": { - "type": "createdElement", - "element": "div#Netaff-yh1XbS0vK3NaRGu" - }, - "receiverElement": { - "type": "domElement", - "element": "div#Global", - "tree": { - "body": { - "div#Global": 1 - } - } - } - }, - { - "insertedElement": { - "type": "createdElement", - "element": "img" - }, - "receiverElement": { - "type": "body" - } - } - ] - }); - }); - - - it('should transform DOMqueriesWithoutResults offenders', function() { - results = rulesChecker.check({ - "toolsResults": { - "phantomas": { - "metrics": { - "DOMqueriesWithoutResults": 2 - }, - "offenders": { - "DOMqueriesWithoutResults": [ - "#SearchMenu (in #document) using getElementById", - ".partnership-link (in body > div#Global > div#Header > ul#MainMenu) using getElementsByClassName" - ] - } - } - } - }, policies); - - results.should.have.a.property('DOMqueriesWithoutResults'); - results.DOMqueriesWithoutResults.should.have.a.property('offendersObj').that.deep.equals({ - "count": 2, - "list": [ - { - "context": { - "type": "document" - }, - "fn": "getElementById", - "query": "#SearchMenu " - }, - { - "context": { - "element": "ul#MainMenu", - "tree": { - "body": { - "div#Global": { - "div#Header": { - "ul#MainMenu": 1 - } - } - } - }, - "type": "domElement" - }, - "fn": "getElementsByClassName", - "query": ".partnership-link " - } - ] - }); - }); - it('should transform DOMqueriesAvoidable offenders', function() { results = rulesChecker.check({ @@ -246,61 +121,6 @@ describe('rulesChecker', function() { }); - it('should transform eventsBound offenders', function() { - results = rulesChecker.check({ - "toolsResults": { - "phantomas": { - "metrics": { - "eventsBound": 2 - }, - "offenders": { - "eventsBound": [ - "\"DOMContentLoaded\" bound to \"#document\"", - "\"unload\" bound to \"window\"", - "\"submit\" bound to \"body > div#Global > div#Header > form#search_mini_form\"" - ] - } - } - } - }, policies); - - results.should.have.a.property('eventsBound'); - results.eventsBound.should.have.a.property('offendersObj').that.deep.equals({ - "count": 3, - "list": [ - { - "element": { - "type": "document" - }, - "eventName": "DOMContentLoaded" - }, - { - "element": { - "type": "window" - }, - "eventName": "unload" - }, - { - "element": { - "element": "form#search_mini_form", - "tree": { - "body": { - "div#Global": { - "div#Header": { - "form#search_mini_form": 1 - } - } - } - }, - "type": "domElement" - }, - "eventName": "submit" - } - ] - }); - }); - - it('should transform jsErrors offenders', function() { results = rulesChecker.check({ "toolsResults": { From 5e3748315c45e19ff7fbddbf31f1a64995650e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 09:22:25 +0200 Subject: [PATCH 11/19] Remove jQueryNotDelegatedEvents offenders and send user to filtered timeline --- front/src/views/rule.html | 20 +++++--------------- lib/metadata/policies.js | 2 +- lib/tools/jsExecutionTransformer.js | 6 ------ 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/front/src/views/rule.html b/front/src/views/rule.html index 5a4c884..9014439 100644 --- a/front/src/views/rule.html +++ b/front/src/views/rule.html @@ -71,21 +71,6 @@ function {{offender.functionName}} used {{offender.count}} times
-
- function {{offender.functionName}} used on {{offender.contextLength}} DOM elements without event delegation -
no backtrace
-
- backtrace -
-
- {{obj.functionName}}() - - line {{obj.line}} -
-
-
-
-
{{offender.writeFn}} @@ -219,6 +204,11 @@ Please open the JS timeline, filtered by "Queries without results"
+
+

{{rule.value}} offenders

+ Please open the JS timeline, filtered by "Events not delegated" +
+

404

Rule "{{policyName}}"" not found diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index 8c44614..e54169b 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -358,7 +358,7 @@ var policies = { "isOkThreshold": 1, "isBadThreshold": 100, "isAbnormalThreshold": 180, - "hasOffenders": true + "hasOffenders": false }, "cssParsingErrors": { "tool": "phantomas", diff --git a/lib/tools/jsExecutionTransformer.js b/lib/tools/jsExecutionTransformer.js index 9beff21..595a913 100644 --- a/lib/tools/jsExecutionTransformer.js +++ b/lib/tools/jsExecutionTransformer.js @@ -25,7 +25,6 @@ var jsExecutionTransformer = function() { metrics.jQueryNotDelegatedEvents = 0; offenders.jQueryFunctionsUsed = []; - offenders.jQueryNotDelegatedEvents = []; } try { @@ -40,11 +39,6 @@ var jsExecutionTransformer = function() { if (isABindWithoutEventDelegation(node, contextLength)) { metrics.jQueryNotDelegatedEvents += contextLength; - offenders.jQueryNotDelegatedEvents.push({ - functionName: node.data.type.substring(9), - contextLength: contextLength, - backtrace: offendersHelpers.backtraceToArray(node.data.backtrace) - }); node.warning = true; node.eventNotDelegated = true; } From 1aa9cd032fa38ab99a6df16ee333ab26f0aa882d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 09:22:45 +0200 Subject: [PATCH 12/19] Update dependencies --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index cfc5ee6..4b13aff 100644 --- a/package.json +++ b/package.json @@ -11,18 +11,18 @@ }, "main": "./lib/index.js", "dependencies": { - "async": "~0.9.0", + "async": "~1.0.0", "body-parser": "~1.12.4", - "compression": "~1.4.3", + "compression": "~1.4.4", "cors": "^2.6.0", "debug": "~2.2.0", - "express": "~4.12.3", + "express": "~4.12.4", "lwip": "0.0.6", "meow": "^3.1.0", "phantomas": "1.10.2", "ps-node": "0.0.4", - "q": "~1.4.0", - "rimraf": "~2.3.3", + "q": "~1.4.1", + "rimraf": "~2.3.4", "temporary": "0.0.8" }, "devDependencies": { @@ -47,7 +47,7 @@ "grunt-usemin": "^3.0.0", "grunt-webfont": "^0.5.3", "matchdep": "^0.3.0", - "mocha": "^2.2.4", + "mocha": "^2.2.5", "request": "^2.55.0", "sinon": "^1.14.1", "sinon-chai": "^2.7.0" From 1e60a4307599c9a53db0dbed803b8058a6970789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 11:42:47 +0200 Subject: [PATCH 13/19] Don't count duplicated queries on elements not attached to DOM --- .../phantomas/custom_modules/modules/domQYLT/domQYLT.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/tools/phantomas/custom_modules/modules/domQYLT/domQYLT.js b/lib/tools/phantomas/custom_modules/modules/domQYLT/domQYLT.js index cbae27f..4d71dac 100644 --- a/lib/tools/phantomas/custom_modules/modules/domQYLT/domQYLT.js +++ b/lib/tools/phantomas/custom_modules/modules/domQYLT/domQYLT.js @@ -365,7 +365,12 @@ exports.module = function(phantomas) { phantomas.log('DOM query: by %s - "%s" (using %s) in %s', type, query, fnName, context); phantomas.incrMetric('DOMqueries'); - if (context && context.indexOf('DocumentFragment') === -1) { + if (context && ( + context.indexOf('html') === 0 || + context.indexOf('body') === 0 || + context.indexOf('head') === 0 || + context.indexOf('#document') === 0 + )) { DOMqueries.push(type + ' "' + query + '" with ' + fnName + ' (in context ' + context + ')'); } }); From cb46274288f242e008c93f0588ef261227484b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 13:33:26 +0200 Subject: [PATCH 14/19] New rule: jQuery calls on empty objects --- front/src/views/rule.html | 5 +++++ lib/metadata/policies.js | 9 +++++++++ lib/metadata/scoreProfileGeneric.json | 5 +++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/front/src/views/rule.html b/front/src/views/rule.html index 9014439..72635fa 100644 --- a/front/src/views/rule.html +++ b/front/src/views/rule.html @@ -204,6 +204,11 @@ Please open the JS timeline, filtered by "Queries without results"
+
+

{{rule.value}} offenders

+ Please open the JS timeline, filtered by "jQuery calls on empty object" +
+

{{rule.value}} offenders

Please open the JS timeline, filtered by "Events not delegated" diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index e54169b..6cc6045 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -351,6 +351,15 @@ var policies = { "isAbnormalThreshold": 0, "hasOffenders": true }, + "jQueryCallsOnEmptyObject": { + "tool": "jsExecutionTransformer", + "label": "Calls on empty objects", + "message": "

This metric counts the number of jQuery functions called on an empty jQuery object. The call was useless.

This can be helpful to detect dead or unused code.

", + "isOkThreshold": 1, + "isBadThreshold": 100, + "isAbnormalThreshold": 180, + "hasOffenders": false + }, "jQueryNotDelegatedEvents": { "tool": "jsExecutionTransformer", "label": "Events not delegated", diff --git a/lib/metadata/scoreProfileGeneric.json b/lib/metadata/scoreProfileGeneric.json index 77af6ee..c5def7f 100644 --- a/lib/metadata/scoreProfileGeneric.json +++ b/lib/metadata/scoreProfileGeneric.json @@ -36,9 +36,10 @@ "jQuery": { "label": "jQuery", "policies": { - "jQueryVersion": 1, - "jQueryVersionsLoaded": 1, + "jQueryVersion": 2, + "jQueryVersionsLoaded": 2, "jQueryFunctionsUsed": 1, + "jQueryCallsOnEmptyObject": 1, "jQueryNotDelegatedEvents": 1 } }, From 4b098aef692ee516dd8dcf0d86f512a0e3fdd855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 14:04:27 +0200 Subject: [PATCH 15/19] Fix timeline bug, not being displayed --- front/src/views/timeline.html | 16 ++++++------- lib/tools/jsExecutionTransformer.js | 35 ++++++++++++++++------------- test/core/indexTest.js | 12 +++++++--- test/www/simple-page.html | 4 ++++ 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/front/src/views/timeline.html b/front/src/views/timeline.html index e0bc1e0..b774c9a 100644 --- a/front/src/views/timeline.html +++ b/front/src/views/timeline.html @@ -16,14 +16,14 @@
diff --git a/lib/tools/jsExecutionTransformer.js b/lib/tools/jsExecutionTransformer.js index 595a913..1015a09 100644 --- a/lib/tools/jsExecutionTransformer.js +++ b/lib/tools/jsExecutionTransformer.js @@ -10,6 +10,11 @@ var jsExecutionTransformer = function() { var jQueryFunctionsCollection = new Collection(); var metrics = { + domInteractive: 0, + domContentLoaded: 0, + domContentLoadedEnd: 0, + domComplete: 0, + DOMaccesses: 0, DOMaccessesOnScroll: 0, queriesWithoutResults: 0 @@ -68,22 +73,22 @@ var jsExecutionTransformer = function() { // Mark a performance flag if (['domInteractive', 'domContentLoaded', 'domContentLoadedEnd', 'domComplete'].indexOf(node.data.type) >= 0) { node.windowPerformance = true; - } - // Read the execution tree and adjust the navigation timings (cause their not very well synchronised) - switch(node.data.type) { - case 'domInteractive': - data.toolsResults.phantomas.metrics.domInteractive = node.data.timestamp; - break; - case 'domContentLoaded': - data.toolsResults.phantomas.metrics.domContentLoaded = node.data.timestamp; - break; - case 'domContentLoadedEnd': - data.toolsResults.phantomas.metrics.domContentLoadedEnd = node.data.timestamp; - break; - case 'domComplete': - data.toolsResults.phantomas.metrics.domComplete = node.data.timestamp; - break; + // Adjust the navigation timings (cause their not very well synchronised) + switch(node.data.type) { + case 'domInteractive': + javascriptExecutionTree.data.domInteractive = node.data.timestamp; + break; + case 'domContentLoaded': + javascriptExecutionTree.data.domContentLoaded = node.data.timestamp; + break; + case 'domContentLoadedEnd': + javascriptExecutionTree.data.domContentLoadedEnd = node.data.timestamp; + break; + case 'domComplete': + javascriptExecutionTree.data.domComplete = node.data.timestamp; + break; + } } // Transform domPaths into objects diff --git a/test/core/indexTest.js b/test/core/indexTest.js index 757c1aa..66d9a07 100644 --- a/test/core/indexTest.js +++ b/test/core/indexTest.js @@ -53,8 +53,9 @@ describe('index.js', function() { data.toolsResults.phantomas.metrics.should.have.a.property('requests').that.equals(1); data.toolsResults.phantomas.should.have.a.property('offenders').that.is.an('object'); data.toolsResults.phantomas.offenders.should.have.a.property('DOMelementMaxDepth'); - data.toolsResults.phantomas.offenders.DOMelementMaxDepth.should.have.length(1); + data.toolsResults.phantomas.offenders.DOMelementMaxDepth.should.have.length(2); data.toolsResults.phantomas.offenders.DOMelementMaxDepth[0].should.equal('body > h1[0]'); + data.toolsResults.phantomas.offenders.DOMelementMaxDepth[1].should.equal('body > script[1]'); // Test rules data.should.have.a.property('rules').that.is.an('object'); @@ -76,10 +77,11 @@ describe('index.js', function() { "score": 100, "abnormalityScore": 0, "offendersObj": { - "count": 1, + "count": 2, "tree": { "body": { - "h1[0]": 1 + "h1[0]": 1, + "script[1]": 1 } } } @@ -91,6 +93,10 @@ describe('index.js', function() { data.should.have.a.property('javascriptExecutionTree').that.is.an('object'); data.javascriptExecutionTree.should.have.a.property('data'); data.javascriptExecutionTree.data.should.have.a.property('type').that.equals('main'); + data.javascriptExecutionTree.data.should.have.a.property('domInteractive').that.is.a('number'); + data.javascriptExecutionTree.data.should.have.a.property('domContentLoaded').that.is.a('number'); + data.javascriptExecutionTree.data.should.have.a.property('domContentLoadedEnd').that.is.a('number'); + data.javascriptExecutionTree.data.should.have.a.property('domComplete').that.is.a('number'); /*jshint expr: true*/ console.log.should.not.have.been.called; diff --git a/test/www/simple-page.html b/test/www/simple-page.html index 83c658d..b7090c1 100644 --- a/test/www/simple-page.html +++ b/test/www/simple-page.html @@ -4,5 +4,9 @@

Simple page

+ + \ No newline at end of file From a97dcc5efbcf30236eeedad73d3823d38ad0ee33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 14:35:39 +0200 Subject: [PATCH 16/19] Change the tweet-this message --- front/src/js/controllers/dashboardCtrl.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/front/src/js/controllers/dashboardCtrl.js b/front/src/js/controllers/dashboardCtrl.js index 0b5732e..8efc959 100644 --- a/front/src/js/controllers/dashboardCtrl.js +++ b/front/src/js/controllers/dashboardCtrl.js @@ -25,8 +25,7 @@ dashboardCtrl.controller('DashboardCtrl', ['$scope', '$rootScope', '$routeParams // By default, Angular sorts object's attributes alphabetically. Countering this problem by retrieving the keys order here. $scope.categoriesOrder = Object.keys($scope.result.scoreProfiles.generic.categories); - $scope.globalScore = Math.max($scope.result.scoreProfiles.generic.globalScore, 0); - $scope.tweetText = 'My website\'s score is ' + $scope.globalScore + '/100 on #YellowLabTools!'; + $scope.tweetText = 'I\'ve discovered this cool open-source tool that audits the front-end quality of a web page: '; } $scope.showRulePage = function(ruleName) { @@ -37,18 +36,18 @@ dashboardCtrl.controller('DashboardCtrl', ['$scope', '$rootScope', '$routeParams API.relaunchTest($scope.result); }; - /// When comming from a social shared link, the user needs to click on "See full report" button to display the full dashboard. + // When comming from a social shared link, the user needs to click on "See full report" button to display the full dashboard. $scope.seeFullReport = function() { $scope.fromSocialShare = false; $location.search({}); }; $scope.shareOnTwitter = function(message) { - openSocialPopup('https://twitter.com/intent/tweet?url=' + document.URL + '%3Fshare&text=' + encodeURIComponent(message)); + openSocialPopup('https://twitter.com/intent/tweet?text=' + encodeURIComponent(message + 'http://yellowlab.tools')); }; $scope.shareOnLinkedin = function(message) { - openSocialPopup('https://www.linkedin.com/shareArticle?mini=true&url=' + document.URL + '%3Fshare&title=' + encodeURIComponent(message) + '&summary=' + encodeURIComponent('YellowLabTools is a free online tool that analyzes performance and front-end quality of a webpage.')); + openSocialPopup('https://www.linkedin.com/shareArticle?mini=true&url=http://yellowlab.tools&title=' + encodeURIComponent(message) + '&summary=' + encodeURIComponent('YellowLabTools is a free online tool that analyzes performance and front-end quality of a webpage.')); }; function openSocialPopup(url) { From 22ab7515fbe20499da24c01b0248ea9ae6c34a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 15:01:21 +0200 Subject: [PATCH 17/19] Redirect www. to non-www (for SEO) --- bin/server.js | 2 ++ lib/server/middlewares/wwwRedirectMiddleware.js | 12 ++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 lib/server/middlewares/wwwRedirectMiddleware.js diff --git a/bin/server.js b/bin/server.js index a345910..9ad04a8 100644 --- a/bin/server.js +++ b/bin/server.js @@ -7,12 +7,14 @@ var cors = require('cors'); var authMiddleware = require('../lib/server/middlewares/authMiddleware'); var apiLimitsMiddleware = require('../lib/server/middlewares/apiLimitsMiddleware'); +var wwwRedirectMiddleware = require('../lib/server/middlewares/wwwRedirectMiddleware'); // Middlewares app.use(compress()); app.use(bodyParser.json()); app.use(cors()); +app.use(wwwRedirectMiddleware); app.use(authMiddleware); app.use(apiLimitsMiddleware); diff --git a/lib/server/middlewares/wwwRedirectMiddleware.js b/lib/server/middlewares/wwwRedirectMiddleware.js new file mode 100644 index 0000000..4560222 --- /dev/null +++ b/lib/server/middlewares/wwwRedirectMiddleware.js @@ -0,0 +1,12 @@ +var wwwRedirectMiddleware = function(req, res, next) { + 'use strict'; + + // Redirect www.yellowlab.tools to yellowlab.tools without "www" (for SEO purpose) + if(/^www\.yellowlab\.tools/.test(req.headers.host)) { + res.redirect(301, req.protocol + '://' + req.headers.host.replace(/^www\./, '') + req.url); + } else { + next(); + } +}; + +module.exports = wwwRedirectMiddleware; \ No newline at end of file From 090a53b95ae18078cbb19b1a6684ef3c1bab6792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 15:57:01 +0200 Subject: [PATCH 18/19] Killing a Phantomas process will not kill YLT anymore --- lib/server/datastores/runsDatastore.js | 6 ++++ lib/tools/phantomas/phantomasWrapper.js | 37 +++++++++++++++++++------ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/lib/server/datastores/runsDatastore.js b/lib/server/datastores/runsDatastore.js index 15b4ada..8104b68 100644 --- a/lib/server/datastores/runsDatastore.js +++ b/lib/server/datastores/runsDatastore.js @@ -58,6 +58,9 @@ function RunsDatastore() { var errorMessage; switch(err) { + case '1': + errorMessage = "Error 1: unknown error"; + break; case '252': errorMessage = "Error 252: page timeout in Phantomas"; break; @@ -76,6 +79,9 @@ function RunsDatastore() { case '1002': errorMessage = "Error 1002: missing Phantomas metrics"; break; + case '1003': + errorMessage = "Error 1003: Phantomas not returning"; + break; default: errorMessage = err; } diff --git a/lib/tools/phantomas/phantomasWrapper.js b/lib/tools/phantomas/phantomasWrapper.js index f4d85a0..1e2a74a 100644 --- a/lib/tools/phantomas/phantomasWrapper.js +++ b/lib/tools/phantomas/phantomasWrapper.js @@ -19,6 +19,7 @@ var PhantomasWrapper = function() { var options = { + // Cusomizable options 'timeout': task.options.timeout || 45, 'js-deep-analysis': task.options.jsDeepAnalysis || false, @@ -75,34 +76,52 @@ var PhantomasWrapper = function() { } debug('node node_modules/phantomas/bin/phantomas.js --url=' + task.url + optionsString + ' --verbose'); - // Kill the application if nothing happens + var phantomasPid; + var isKilled = false; + + // Kill phantomas if nothing happens var killer = setTimeout(function() { - debug('Killing the app because the test on %s was launched %d seconds ago', task.url, 5*options.timeout); - // If in server mode, forever will restart the server + console.log('Killing phantomas because the test on ' + task.url + ' was launched ' + 5*options.timeout + ' seconds ago'); - // Kill the Phantomas process first if (phantomasPid) { ps.kill(phantomasPid, function(err) { + if (err) { debug('Could not kill Phantomas process %s', phantomasPid); - } - else { - debug('Phantomas process %s was correctly killed', phantomasPid); + + // Suicide + process.exit(1); + // If in server mode, forever will restart the server } - // Then suicide. - process.exit(1); + debug('Phantomas process %s was correctly killed', phantomasPid); + + // Then mark the test as failed + // Error 1003 = Phantomas not answering + deferred.reject(1003); + isKilled = true; }); + } else { + // Suicide + process.exit(1); } }, 5*options.timeout*1000); + // It's time to launch the test!!! var triesNumber = 2; async.retry(triesNumber, function(cb) { var process = phantomas(task.url, options, function(err, json, results) { + + if (isKilled) { + debug('Process was killed, too late Phantomas, sorry...'); + return; + } + + debug('Returning from Phantomas'); // Adding some YellowLabTools errors here From f553d453963b226ef0f6ac75bcb7846f7c8cff4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20M=C3=A9tais?= Date: Mon, 25 May 2015 16:01:24 +0200 Subject: [PATCH 19/19] v1.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b13aff..aaed4cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yellowlabtools", - "version": "1.5.1", + "version": "1.6.1", "description": "Online tool to audit a webpage for performance and front-end quality issues", "repository": { "type": "git",