diff --git a/front/src/css/rule.css b/front/src/css/rule.css index 2a2dce0..d346bca 100644 --- a/front/src/css/rule.css +++ b/front/src/css/rule.css @@ -109,6 +109,7 @@ border-bottom-left-radius: 0.4em; border-bottom-right-radius: 0.4em; border-top: 1px solid #999; + z-index: 2; } .offenders .offenderButton .domTree { text-align: left; diff --git a/front/src/css/timeline.css b/front/src/css/timeline.css index 9c3a0cd..f299479 100644 --- a/front/src/css/timeline.css +++ b/front/src/css/timeline.css @@ -268,7 +268,7 @@ input.textFilter { background: #fff; border: 2px solid #f1c40f; border-radius: 0.5em; - z-index: 1; + z-index: 2; } @media screen and (max-width: 1024px) { .detailsOverlay { diff --git a/front/src/js/controllers/timelineCtrl.js b/front/src/js/controllers/timelineCtrl.js index 4351ae6..2d352d3 100644 --- a/front/src/js/controllers/timelineCtrl.js +++ b/front/src/js/controllers/timelineCtrl.js @@ -187,6 +187,15 @@ timelineCtrl.controller('TimelineCtrl', ['$scope', '$rootScope', '$routeParams', node.showDetails = !isOpen; }; + $scope.isStringOfObject = function(str) { + return typeof str === 'string' && str[0] === '{' && str[str.length - 1] === '}'; + }; + + $scope.isPureString = function(str) { + return typeof str === 'string' && str[0] !== '{' && str !== '(function)' && str !== '[Object]' && str !== '[Array]' && str !== 'true' && str !== 'false' && str !== 'undefined' && str !== 'unknown'; + }; + + $scope.backToDashboard = function() { $location.path('/result/' + $scope.runId); }; diff --git a/front/src/js/directives/offendersDirectives.js b/front/src/js/directives/offendersDirectives.js index cb691d0..e430ddf 100644 --- a/front/src/js/directives/offendersDirectives.js +++ b/front/src/js/directives/offendersDirectives.js @@ -43,6 +43,17 @@ offendersDirectives.directive('domElementButton', function() { }; }); +offendersDirectives.directive('jqueryContextButton', function() { + return { + restrict: 'E', + scope: { + context: '=' + }, + templateUrl: 'views/jqueryContextButton.html', + replace: true + }; +}); + offendersDirectives.filter('shortenUrl', function() { return function(url, maxLength) { if (!maxLength) { @@ -104,8 +115,6 @@ offendersDirectives.directive('offenderButton', function() { restrict: 'C', link: function(scope, element, attrs) { - console.log('initializing touchstart'); - element.bind('touchstart mouseenter', function(e) { element.addClass('mouseOver'); e.preventDefault(); diff --git a/front/src/less/rule.less b/front/src/less/rule.less index ce68297..82f8150 100644 --- a/front/src/less/rule.less +++ b/front/src/less/rule.less @@ -120,6 +120,7 @@ border-bottom-left-radius: 0.4em; border-bottom-right-radius: 0.4em; border-top: 1px solid #999; + z-index: 2; } .domTree { diff --git a/front/src/less/timeline.less b/front/src/less/timeline.less index dea012e..17525aa 100644 --- a/front/src/less/timeline.less +++ b/front/src/less/timeline.less @@ -309,7 +309,7 @@ input.textFilter { background: #fff; border: 2px solid #f1c40f; border-radius: 0.5em; - z-index: 1; + z-index: 2; } @media screen and (max-width: 1024px) { .detailsOverlay { diff --git a/front/src/views/domElementButton.html b/front/src/views/domElementButton.html index 78dda24..268f4cd 100644 --- a/front/src/views/domElementButton.html +++ b/front/src/views/domElementButton.html @@ -3,5 +3,6 @@ DOM element {{obj.element}} Fragment element {{obj.element}} Created element {{obj.element}} + Incorrect element \ No newline at end of file diff --git a/front/src/views/jqueryContextButton.html b/front/src/views/jqueryContextButton.html new file mode 100644 index 0000000..0366081 --- /dev/null +++ b/front/src/views/jqueryContextButton.html @@ -0,0 +1,9 @@ + + Empty jQuery object + + + {{context.length}} elements + (, + and {{context.length - 2}} more...) + + \ No newline at end of file diff --git a/front/src/views/timeline.html b/front/src/views/timeline.html index 4a8c0bd..e96f971 100644 --- a/front/src/views/timeline.html +++ b/front/src/views/timeline.html @@ -93,10 +93,237 @@
- {{node.data.callDetails.arguments[0]}} - : {{node.data.callDetails.arguments[1]}} - : {{node.data.callDetails.arguments[2]}} - : {{node.data.callDetails.arguments[3]}} + + {{node.data.callDetails.arguments[0]}} + + + {{node.data.callDetails.arguments[0]}} on + + + appended to + + + insert in before + + + + + + + + {{node.data.callDetails.arguments[0]}} bound to + + + + {{node.data.callDetails.arguments[0]}} + + + (function) + + + {{node.data.callDetails.arguments[0]}} + on + + + {{node.data.callDetails.arguments[0]}} + on + + + + + + set content "{{node.data.callDetails.arguments[0]}}" to + get content from + + + append {{node.data.callDetails.arguments[0]}}, {{node.data.callDetails.arguments[1]}}, {{node.data.callDetails.arguments[2]}}, and more... to + + + append to {{node.data.callDetails.arguments[0]}} + + + prepend {{node.data.callDetails.arguments[0]}}, {{node.data.callDetails.arguments[1]}}, {{node.data.callDetails.arguments[2]}}, and more... to + + + prepend to {{node.data.callDetails.arguments[0]}} + + + insert {{node.data.callDetails.arguments[0]}}, {{node.data.callDetails.arguments[1]}}, {{node.data.callDetails.arguments[2]}}, and more... before + + + insert before {{node.data.callDetails.arguments[0]}} + + + insert {{node.data.callDetails.arguments[0]}}, {{node.data.callDetails.arguments[1]}}, {{node.data.callDetails.arguments[2]}}, and more... after + + + insert after {{node.data.callDetails.arguments[0]}} + + + remove filtered by {{node.data.callDetails.arguments[0]}} + + + detach filtered by {{node.data.callDetails.arguments[0]}} + + + empty + + + clone + + + replace with {{node.data.callDetails.arguments[0]}} + + + replace {{node.data.callDetails.arguments[0]}} with + + + set text "{{node.data.callDetails.arguments[0]}}" to + get text from + + + wrap with {{node.data.callDetails.arguments[0]}} + + + wrap the content of with {{node.data.callDetails.arguments[0]}} + + + unwrap + + + set {{node.data.callDetails.arguments[0]}} to + + get {{node.data.callDetails.arguments[0]}} from + set {{node.data.callDetails.arguments[0]}} : {{node.data.callDetails.arguments[1]}} to + + + + get from + set {{node.data.callDetails.arguments[0]}} to + + + get from + + + + + + + to visible + to hidden + + + bind {{node.data.callDetails.arguments[0]}} on 's children filtered by {{node.data.callDetails.arguments[1]}} + + + + unbind {{node.data.callDetails.arguments[0]}} from 's children filtered by {{node.data.callDetails.arguments[1]}} + + + unbind all events + + + + bind {{node.data.callDetails.arguments[0]}} on + + + + unbind {{node.data.callDetails.arguments[0]}} from + + + unbind all events + + + + bind {{node.data.callDetails.arguments[1]}} on 's children filtered by {{node.data.callDetails.arguments[0]}} + + + + unbind {{node.data.callDetails.arguments[1]}} from 's children filtered by {{node.data.callDetails.arguments[0]}} + + + unbind namespace {{node.data.callDetails.arguments[0]}} + + + unbind all events + + + + bind {{node.data.callDetails.arguments[0]}} on + + + + unbind {{node.data.callDetails.arguments[0]}} from + + + unbind all events + + + + bind on + + + remove {{node.data.callDetails.arguments[0]}} from + + + + set value {{node.data.callDetails.arguments[0]}} to + + + get value from + + + + {{node.data.callDetails.arguments[0]}} on + + + magic no-argument toggleClass + toggle {{node.data.callDetails.arguments[0]}} + on + to {{node.data.callDetails.arguments[1]}} + + + of + filtered by {{node.data.callDetails.arguments[0]}} + + + closest {{node.data.callDetails.arguments[0]}} from + in context {{node.data.callDetails.arguments[1]}} + + + after + matching {{node.data.callDetails.arguments[0]}} + + + after + until {{node.data.callDetails.arguments[0]}} + and matching {{node.data.callDetails.arguments[1]}} + + + of + + + before + matching {{node.data.callDetails.arguments[0]}} + + + before + until {{node.data.callDetails.arguments[0]}} + and matching {{node.data.callDetails.arguments[1]}} + + + of + matching {{node.data.callDetails.arguments[0]}} + + + of + until {{node.data.callDetails.arguments[0]}} + and matching {{node.data.callDetails.arguments[1]}} + + + near + matching {{node.data.callDetails.arguments[0]}} +
@@ -107,11 +334,6 @@
-
-

Called on DOM element

-
{{node.data.callDetails.context.domElement}}
-
-

Called on 0 jQuery element

Useless function call, as the jQuery object is empty.

@@ -133,6 +355,9 @@

The query returned 0 results. Could it be unused or dead code?

+

+ The query returned . +

Backtrace

diff --git a/lib/offendersHelpers.js b/lib/offendersHelpers.js index f2dde54..92405d9 100644 --- a/lib/offendersHelpers.js +++ b/lib/offendersHelpers.js @@ -32,6 +32,15 @@ var OffendersHelpers = function() { }; this.domPathToDomElementObj = function(domPath) { + + if (typeof domPath === 'boolean') { + return { + // Not a normal element path + type: 'notAnElement', + element: domPath + }; + } + var domArray = this.domPathToArray(domPath); var domTree = this.listOfDomArraysToTree([this.domPathToArray(domPath)]); diff --git a/lib/tools/jsExecutionTransformer.js b/lib/tools/jsExecutionTransformer.js index 587fe97..e9e5312 100644 --- a/lib/tools/jsExecutionTransformer.js +++ b/lib/tools/jsExecutionTransformer.js @@ -51,9 +51,17 @@ var jsExecutionTransformer = function() { // Change the list of dom paths into a tree treeRecursiveParser(node, function(node) { + if (node.data.callDetails && node.data.callDetails.context && node.data.callDetails.context.length > 0) { - var domArrays = node.data.callDetails.context.elements.map(offendersHelpers.domPathToArray); - node.data.callDetails.context.elements = offendersHelpers.listOfDomArraysToTree(domArrays); + node.data.callDetails.context.elements = node.data.callDetails.context.elements.map(offendersHelpers.domPathToDomElementObj, offendersHelpers); + } + + if (node.data.type === 'appendChild' || node.data.type === 'insertBefore') { + node.data.callDetails.arguments[0] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[0]); + } + + if (node.data.type === 'insertBefore') { + node.data.callDetails.arguments[1] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[1]); } }); }); diff --git a/lib/tools/phantomas/custom_modules/modules/domQYLT/domQYLT.js b/lib/tools/phantomas/custom_modules/modules/domQYLT/domQYLT.js index ab23f0d..e67aaf6 100644 --- a/lib/tools/phantomas/custom_modules/modules/domQYLT/domQYLT.js +++ b/lib/tools/phantomas/custom_modules/modules/domQYLT/domQYLT.js @@ -247,11 +247,12 @@ exports.module = function(phantomas) { }); } - function insertBeforeSpyBefore(child) { + function insertBeforeSpyBefore(child, refElement) { /*jshint validthis: true */ var context = phantomas.getDOMPath(this); var appended = phantomas.getDOMPath(child); + var referent = phantomas.getDOMPath(refElement); appendChild(child, this, context, appended); phantomas.enterContext({ @@ -261,7 +262,10 @@ exports.module = function(phantomas) { length: 1, elements: [context] }, - arguments: [appended] + arguments: [ + appended, + referent + ] }, backtrace: phantomas.getBacktrace() }); diff --git a/lib/tools/phantomas/custom_modules/modules/jQYLT/jQYLT.js b/lib/tools/phantomas/custom_modules/modules/jQYLT/jQYLT.js index 23b36dd..cdb1134 100644 --- a/lib/tools/phantomas/custom_modules/modules/jQYLT/jQYLT.js +++ b/lib/tools/phantomas/custom_modules/modules/jQYLT/jQYLT.js @@ -116,8 +116,9 @@ exports.module = function(phantomas) { 'addClass', 'removeClass', 'toggleClass', + ]; - // Tree traversal + var jQueryTraversalFunctions = [ 'children', 'closest', 'find', @@ -134,9 +135,11 @@ exports.module = function(phantomas) { 'siblings' ]; + jQueryFunctions = jQueryFunctions.concat(jQueryTraversalFunctions); + // spy calls to jQuery functions phantomas.once('init', function() { - phantomas.evaluate(function(jQueryFunctions) { + phantomas.evaluate(function(jQueryFunctions, jQueryTraversalFunctions) { (function(phantomas) { var jQuery; var oldJQuery; @@ -219,10 +222,6 @@ exports.module = function(phantomas) { phantomas.enterContext({ type: 'jQuery - find', callDetails: { - context: { - length: 1, - elements: ['#document'] - }, arguments: [selector] }, backtrace: phantomas.getBacktrace() @@ -234,8 +233,7 @@ exports.module = function(phantomas) { var data = phantomas.getContextData(); if (data.type === 'jQuery - find' && - data.callDetails.context.elements.length === 1 && - data.callDetails.context.elements[0] === '#document' && + !data.callDetails.context && data.callDetails.arguments.length === 1 && /^#([\w\-]*)$/.exec(data.callDetails.arguments[0]) !== null) { @@ -284,6 +282,11 @@ exports.module = function(phantomas) { args = [].slice.call(arguments); args.forEach(function(arg, index) { + + if (arg instanceof Array) { + arg = '[Array]'; + } + if (arg instanceof Object) { if (arg instanceof jQuery || (arg.jquery && arg.jquery.length > 0)) { @@ -354,12 +357,19 @@ exports.module = function(phantomas) { }); }, function(result) { - phantomas.leaveContext(); + if (jQueryTraversalFunctions.indexOf(functionName) >= 0) { + var moreData = { + resultsNumber : (result && result.length) ? result.length : 0 + }; + phantomas.leaveContext(moreData); + } else { + phantomas.leaveContext(); + } }) || phantomas.log('jQuery: can not track jQuery - ' + functionName + ' (this version of jQuery doesn\'t support it)'); }); }); })(window.__phantomas); - }, jQueryFunctions); + }, jQueryFunctions, jQueryTraversalFunctions); });