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/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,d09GRgABAAAAABF0AA4AAAAAK9AAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABRAAAAEMAAABWQLtM2mNtYXAAAAGIAAAAOgAAAUriHBC2Y3Z0IAAAAcQAAAAUAAAAIv+YA5pmcGdtAAAB2AAABacAAAuX2BTb8Gdhc3AAAAeAAAAACAAAAAgAAAAQZ2x5ZgAAB4gAAAdJAAAZuvXTIk1oZWFkAAAO1AAAADIAAAA2A+vwjmhoZWEAAA8IAAAAIAAAACQD/QHAaG10eAAADygAAAAPAAAALBYAAABsb2NhAAAPOAAAABgAAAAYH8gkR21heHAAAA9QAAAAIAAAACACfQyHbmFtZQAAD3AAAAEdAAAB8hTbwvhwb3N0AAAQkAAAAGIAAAB/4g/aQ3ByZXAAABD0AAAAfQAAAIqMht7EeJxjYGRiYJzAwMrAwejCmMbAwOAOpb8ySDK0MDAwMbAyM2AFAWmuKQwOHxk/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//AA94nM1YS2/b2BU+h9TDki3ZoihST4oPvaz3W35FZuokflBOMrHsyJU9FZ0iMRBkYBSD2VZdFl1MBkUX3aRZtNNi4EWAme5VoP9iNt500V2Bbtou5F5KViczKJxmTBc2ee+1uNA5upffd77vgARwPqAAB4BAgxXs4IBpcMEseMALPuAhACGIQBQkUCAOSZiHDOSgACWoQA0asAjLsA478Bz/rr3JPOyof0giLVESfQzBkCgGjwFAokF6AlQKrRbKqstomUe7zWLXwZZGx5TNocNUBqedU9M6OLPomnG6dAVncjjrnpnVwR3LU545t+cJzBXQy8x5dWCK6GMZnw5sCXmO5XXg4mUq4OcCT8BfwVDQH9LrKFYxEhYjRwlEFLo1FASXBuFwtAvRqDvaCqlfXKRKnRi5hoLiyY1Ndl/95ebGrZWlpYWFer1aLZeLxXw+m02nU6lEIhaTZVEUhGdPj3qH3cd7D7Y3djZ31m6vrN9aX1peWl5eWlhcWFxcqDfqjUa9WqvWatVypVyplIulYqlUzBfyhUI+m8vmctl0Jp3JpFPzqfn5VCKZSCYTsXgsHo/JiqwosiiJkiQKUSEaFRo2NuP1XHahZL3kQgF5N9rzmGyit4kN8g/5KGCyUS9z7CxWyGzLGFtmkwuYqIrf/huKFAzh+41zePGSpl++GM+dTyjqk85oXu07g17H1pduhnF/ueXwBp19Bw6+b5gh4Nm3k8az/0R98XJYmYQl81/6RrRNJsxsGimQPMi7SDBnBj5XYY1gVIMHAFc5sLjkkxoVnzIZ3opHMfFQxkNV1d7kNnPnVXXQOzvr91EdDgBEU/Z1B/axrJ53dlu3LFN23IohAkUjpceRBouVtugJtILNbrXpSbTDlMM+pafQAc5ph1Ofx2mYcU3P6Gl0gXvW5dYzOAtzntk5PYseYLweRq8oOdoLrM/L6nn0Acf7OL2APPgDvF8vYoDQViColzAE4UgorMuiQAOMKMOllTESkbogSW6p1W5fThzt/fb+471HD7TNe2tq8z0pQ82/B2lUr0gaSbpWKderiQzKNpZbRh9t43ipifFGreLjTXwb5VdzdofD/tqY5l6FYjVUa7Fh/5n6zMy38j7anzvcDnI/H/6z0ERs/uz0449PIWYy7neuhnw0EE/Qb//Oair6+/2BAc7Bd1Zz6bffU3sXw+BXc3igBYf4D1XcRJt17QeU09H9IcV6H6Kfb2MwYN1qobCxg8q69oYl+ugVWGgrZTkmZEFRuBtFirCFhaL/O1eI+D+ThZyj3s0VFalEX7AF+flCFwydYVCF0gVFcStEFf3GSJC2WE9uaIb76i/ub1/OZ529Rx9sH94/1LbW7743n/0f2YzOo0I0D0/UT5nj7ZUayolkQ/E1BKyQZ0QPmYgwTTmstX8aWeF+tJq6G9oYnMNo+eaxqUj7lZLC7ha/cAeF4MZgqBqzNnkGsim4M5itAwdX4zbaOAEP0aLGjnvyaHCbV05UDRFqa0geM0nu5RkbQgyx4+XP/b5KhGaYYX5yTvbBXKIjUYa9cTR8zYb4ueGGESnM9JAwnzn7v0pO4ACOKac6vYGuOzi7jh56U3uTIjT3RQpphVJGNjAWM2xgAxUaFGKt5sfWCizpsbOqixTR+RfeSs5SF+YqNzZXMJOfeKsCGtZKhzmpSF14q9LYWwnIlifmKloZmSsd/NWxuQJSvCPhWOQoSbxVvLuA8fjIWyW6kEi4E4TyTi+SHRnBWCgYO7nB6e6rv/7wcLt1W72cA398dHj84fFuu3WwfbC1ee+uqt3WVpvEQl4bGy5ekQ3jBvLKXATtSq1RI0N2U6xAlZtUNU8l33KLZsqOM1XtuxmiCF730tpKNLqitcdL+i2LaCY0+yqKjLuniuR6+E04Yym+ZQ7N0iaG/rsSP9Y8koccjE8iJ0KbS4dDOOsh9M6wP+yb6vWMLxzRoHoOYE6PS4M2dPCvY3L7HYSliBQ+JrJEFCNPiVBS0FTjl8N3CyW5RIVACoekJyOdVBm1jKoEHNGDSYPr9+M8IydGolJE/OiGZrqvfrrdar6ju9V5vLf7wcNWe7t9b21Fa2rXR2S1q8o66UJSSGNJQV+TnDgZDi4qvDpav7IbTtLx8+EZiteqJoaesWktYp9oifN/EXzZ8E9XwldzhLBD/NsfHz2gXLg5Nku/LSLPcj7+GDhSJJ8CJJCmgNaBSk6qcmrSnp2f1OT0uD1bkyhnZlKUs5OinJsUZTk/Kco+H7MbK1A+YHkfq5cw4PcH9hT0l0f1+EjAYIUU5GCEYAYx2q2i0V4hBVnsgii6RYKzz0dpsjx3QvJkucBHNzPRffWz7Za6ejnMDrqdx2OYrd9Z1VSteev6gFa/aguZlugy7+VsdimPDW+dL3M+u5Ks1u0sZ3SJZJuJaDuHU+ycelxfzzDB4f0gM/O1y+NmnFkn42GCDLlNhdxnw09xwA1bvIAo8PgVNyCioedmBi4jFPNv0bcgRQAAAHicY2BkYGAA4tkvkvvi+W2+MnAzMYDAxbR4AQgdyPD/1/8DTGyMB4FcDgawNABIPQvdAAB4nGNgZGBgPPD/AIMeEwMDw/8vTDwMQBEUwA0Ae20Ev3icY2JgYGAiEgMAAjwAFwAAAAAAAdACfAPMBIAGPAb8CM4JcgsiDN0AAQAAAAsAbAAkAAAAAAACAHIAggB3AAABZAuXAAAAAHicXY69TsMwFIVP+odoEAIhMZulC1L6M/YB2pkO2dPESVslceS4lSoxM/MUzDwFz8WJeyUqbOn6O+ceXxvAA34QoFsBhr52q4cbqgv3SXfCA/Kj8BAhnoVHVC/CY7xiIhziCW+cEAxu6YyRCfdwj1q4T/9deED+EB5y+qfwiP6X8BgxvoVDTILRPjV1u9HFsUysZ19ibdu9qdU8mnm91rW2idOZ2p5VeyoWzuUqt6ZSK1M7XZZGNdYcdOqinXPNcjrNxY9SU2GPFIZ/brGBRoEjSiSwV/4fxUxY73RaYY4Is6v+mv3aZxI4nhkzW5xZW5w4e0HXIafOmTGoSCt/t0uX3IZO43sHOin9CDt/q8ESU+78Xz7yr1e/MPVTYgAAAHicbcFBDsIwDARAb5umLXkLJ17kIFNFMjE4Rv0+B67M0EQ/C/1XiDBhRsKCjBUbdlxQCrvbeVV5xC1V9jEr16RtRFKzV35abSrb+yMjmvU87i7Sc3BVifVk760fRF+VyBj+AAB4nGPw3sFwIihiIyNjX+QGxp0cDBwMyQUbGdicNjEwMmiBGJu5mRg5ICx+BjCL3WkXMwNQmhPI5nDaxeAAYTMzuGxUYewIjNjg0BGxkTnFZaMaiLeLo4GBkcWhIzkkAqQkEgg28zIx8mjtYPzfuoGldyMTUB9rigsAZa0kmwAAAA==") 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/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/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/js/app.js b/front/src/js/app.js index 6a229fb..f981a02 100644 --- a/front/src/js/app.js +++ b/front/src/js/app.js @@ -19,8 +19,20 @@ 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; + 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/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) { diff --git a/front/src/js/controllers/timelineCtrl.js b/front/src/js/controllers/timelineCtrl.js index 3887ff1..c641dc6 100644 --- a/front/src/js/controllers/timelineCtrl.js +++ b/front/src/js/controllers/timelineCtrl.js @@ -19,12 +19,30 @@ 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') + }; + } + function initScriptFiltering() { var offenders = $scope.result.rules.jsCount.offendersObj.list; $scope.scripts = []; 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/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; } 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/index.html b/front/src/views/index.html index aff58e1..f83be65 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 + diff --git a/front/src/views/rule.html b/front/src/views/rule.html index 3e72f34..72635fa 100644 --- a/front/src/views/rule.html +++ b/front/src/views/rule.html @@ -31,22 +31,10 @@ {{offender.id}}: {{offender.occurrences}} occurrences -
- appended to -
- -
- {{offender.query}} (in ) using {{offender.fn}} -
-
{{offender.query}} (in ) using {{offender.fn}}: {{offender.count}} queries
-
- {{offender.eventName}} bound to -
-
Scroll event bound on window Scroll event bound on document @@ -83,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}} @@ -218,10 +191,29 @@
{{offender.color}} ({{offender.occurrences}} times)
- - + +
+

{{rule.value}} offenders

+ Please open the JS timeline +
+ +
+

{{rule.value}} offenders

+ 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" +
+

404

Rule "{{policyName}}"" not found diff --git a/front/src/views/timeline.html b/front/src/views/timeline.html index 497340e..b774c9a 100644 --- a/front/src/views/timeline.html +++ b/front/src/views/timeline.html @@ -16,14 +16,14 @@
@@ -60,11 +60,30 @@

- - + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ {{queryWithoutResultsFilterOn}}
-
+
Type
diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index a203b1c..6cc6045 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -66,82 +66,23 @@ 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 - }, - "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", @@ -175,35 +116,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", @@ -351,9 +263,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 { @@ -389,17 +301,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,12 +345,21 @@ 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, "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", @@ -446,7 +367,7 @@ var policies = { "isOkThreshold": 1, "isBadThreshold": 100, "isAbnormalThreshold": 180, - "hasOffenders": true + "hasOffenders": false }, "cssParsingErrors": { "tool": "phantomas", @@ -1024,7 +945,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, diff --git a/lib/metadata/scoreProfileGeneric.json b/lib/metadata/scoreProfileGeneric.json index aa2590c..c5def7f 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, + "queriesWithoutResults": 1, + "DOMqueriesAvoidable": 1 } }, "scroll": { @@ -38,9 +36,10 @@ "jQuery": { "label": "jQuery", "policies": { - "jQueryVersion": 1, - "jQueryVersionsLoaded": 1, + "jQueryVersion": 2, + "jQueryVersionsLoaded": 2, "jQueryFunctionsUsed": 1, + "jQueryCallsOnEmptyObject": 1, "jQueryNotDelegatedEvents": 1 } }, 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/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 diff --git a/lib/tools/jsExecutionTransformer.js b/lib/tools/jsExecutionTransformer.js index 486cf4b..1015a09 100644 --- a/lib/tools/jsExecutionTransformer.js +++ b/lib/tools/jsExecutionTransformer.js @@ -10,14 +10,17 @@ var jsExecutionTransformer = function() { var jQueryFunctionsCollection = new Collection(); var metrics = { + domInteractive: 0, + domContentLoaded: 0, + domContentLoadedEnd: 0, + domComplete: 0, + DOMaccesses: 0, DOMaccessesOnScroll: 0, queriesWithoutResults: 0 }; - var offenders = { - - }; + var offenders = {}; var hasjQuery = (data.toolsResults.phantomas.metrics.jQueryVersionsLoaded > 0); if (hasjQuery) { @@ -27,7 +30,6 @@ var jsExecutionTransformer = function() { metrics.jQueryNotDelegatedEvents = 0; offenders.jQueryFunctionsUsed = []; - offenders.jQueryNotDelegatedEvents = []; } try { @@ -42,22 +44,19 @@ 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; } if (node.data.resultsNumber === 0) { metrics.queriesWithoutResults ++; + node.queryWithoutResults = true; node.warning = true; } if (contextLength === 0) { metrics.jQueryCallsOnEmptyObject ++; + node.jQueryCallOnEmptyObject = true; node.warning = true; } @@ -74,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/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 + ')'); } }); 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 diff --git a/package.json b/package.json index cfc5ee6..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", @@ -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" 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": { 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