test-unit-sauce
Brian Ford 11 years ago
parent 6cfcaa8545
commit ee422a1957

@ -12,13 +12,16 @@ var scopeCacheEmpty = function () {
var toForward = [
'modelChange',
'scopeChange',
'scopeDeleted',
'watcherChange',
'watchPerfChange',
'applyPerfChange'
];
chrome.extension.onConnect.addListener(function (port) {
port.onMessage.addListener(function (msg) {
port.onMessage.addListener(function (msg) { // [devtools] --> [background]
// notify of page refreshes
if (msg.action === 'register') {
chrome.tabs.onUpdated.addListener(respond);
@ -28,34 +31,36 @@ chrome.extension.onConnect.addListener(function (port) {
function respond (tabId, changeInfo, tab) {
if (tabId === msg.inspectedTabId) {
port.postMessage('refresh');
port.postMessage('refresh'); // [background] --> [devtools]
delete scopeCache[msg.appId]; // clear cache
}
}
// immediately populate the scopes tree from the cache
Object.keys(scopeCache[msg.appId]).forEach(function (scopeId) {
port.postMessage({ // [background] --> [devtools]
action: 'scopeChange',
scope: scopeCache[msg.appId][scopeId],
scopeId: scopeId
});
});
}
});
// [content script] --> [background] --> [devtools]
chrome.extension.onMessage.addListener(function (msg) {
if (toForward.indexOf(msg.action) !== -1) {
port.postMessage(msg);
}
});
// immediately populate the scopes tree from the cache
// TODO: how do we know that the cache refers to the
// tab that we're connected to?
Object.keys(scopeCache).forEach(function (scopeId) {
port.postMessage({
action: 'scopeChange',
scope: scopeCache[scopeId],
scopeId: scopeId
});
});
});
chrome.extension.onMessage.addListener(function (msg) {
chrome.extension.onMessage.addListener(function (msg, sender) {
if (msg.action === 'scopeChange') {
scopeCache[msg.id] = msg.scope;
scopeCache[msg.appId] = scopeCache[msg.appId] || {};
scopeCache[msg.appId][msg.id] = msg.scope;
}
});
});

@ -1,50 +1,64 @@
angular.module('panelApp').controller('ModelCtrl', function ModelCtrl($scope, appContext, appModel) {
angular.module('panelApp').
$scope.modelsExpanded = true;
$scope.watchExpanded = true;
controller('ModelCtrl', function ModelCtrl($scope, appContext, appModel) {
$scope.roots = appModel.getRootScopes;
$scope.selectedRoot = null;
$scope.$watch('roots()', function (newVal, oldVal) {
if (newVal.length > 0 && newVal.indexOf($scope.selectedRoot) === -1) {
$scope.selectedRoot = newVal[0];
}
$scope.rootScopeIds = appModel.getRootScopeIds();
$scope.$on('rootScopeChange', function () {
$scope.rootScopeIds = appModel.getRootScopeIds();
});
$scope.model = null;
$scope.scopeTree = function () {
var select = $scope.selectedRoot;
if (!select) {
var rs = $scope.roots();
if (rs.length === 0) {
return;
$scope.$on('refresh', reset);
function reset () {
$scope.modelsExpanded = true;
$scope.watchExpanded = true;
$scope.selectedRootScopeId = null;
}
reset();
$scope.$watch('rootScopeIds', function (newVal, oldVal) {
if ($scope.selectedRootScopeId == null) {
if ($scope.rootScopeIds.some(function (id) {
return $scope.selectedRootScopeId = id;
})) {
while ($scope.rootScopeIds.indexOf(undefined) !== -1) {
$scope.rootScopeIds.splice($scope.rootScopeIds.indexOf(undefined), 1);
}
}
select = rs[0];
}
return appModel.getScopeTree(select);
};
});
$scope.$watch('selectedRootScopeId', function (newVal) {
$scope.scopeTree = newVal ? appModel.getScopeTree(newVal) : undefined;
});
$scope.selectedScopeId = null;
$scope.watching = {};
appContext.watchModelChange(function (msg) {
$scope.$on('modelChange', function (ev, msg) {
$scope.watching[msg.id] = $scope.watching[msg.id] || {};
Object.keys(msg.changes).forEach(function (key) {
$scope.watching[msg.id][key] = msg.changes[key];
});
Object.keys(msg.changes).
forEach(function (prop) {
$scope.watching[msg.id][prop] = msg.changes[prop];
});
});
appContext.watchWatcherChange(function (msg) {
$scope.$on('watcherChange', function (ev, msg) {
$scope.watchers = msg.watchers;
});
$scope.select = function () {
if ($scope.selectedScopeId === this.val.id) {
if (this.val && $scope.selectedScopeId === this.val.id) {
return;
}
appModel.unwatchModel($scope.selectedScopeId);
delete $scope.watching[$scope.selectedScopeId];
$scope.selectedScopeId = this.val.id;
appModel.watchModel($scope.selectedScopeId);
};

@ -111,6 +111,7 @@ var instument = function instument (window) {
customEvent.initEvent('myCustomEvent', true, true);
var fireCustomEvent = function (data) {
data.appId = instrumentedAppId;
eventProxyElement.innerText = JSON.stringify(data);
eventProxyElement.dispatchEvent(customEvent);
};
@ -160,7 +161,7 @@ var instument = function instument (window) {
};
var popover = null;
var instrumentedAppId = window.location.host + '~' + Math.random();
// Utils
@ -250,6 +251,13 @@ var instument = function instument (window) {
});
}, 50),
scopeDeleted: function (id) {
fireCustomEvent({
action: 'scopeDeleted',
id: id
});
},
watcherChange: throttle(function (id) {
if (debug.modelWatchers[id]) {
fireCustomEvent({
@ -348,6 +356,10 @@ var instument = function instument (window) {
return Object.keys(debug.rootScopes);
},
getAppId: function () {
return instrumentedAppId;
},
fireCustomEvent: fireCustomEvent,
niceNames: function () {
@ -994,6 +1006,7 @@ var instument = function instument (window) {
delete debug[prop][this.$id];
}
}, this);
emit.scopeDeleted(this.$id);
return _destroy.apply(this, arguments);
};

@ -1,6 +1,33 @@
// Broadcast poll events
angular.module('panelApp', []).run(function ($rootScope) {
angular.module('panelApp', []).
run(function ($rootScope, appContext) {
// todo: kill this
setInterval(function () {
$rootScope.$broadcast('poll');
}, 500);
var port = chrome.extension.connect();
port.onMessage.addListener(function (msg) {
if (msg === 'refresh') {
$rootScope.$apply(function () {
$rootScope.$broadcast('refresh');
});
} else if (msg.action) {
$rootScope.$apply(function () {
$rootScope.$broadcast(msg.action, msg);
});
}
});
appContext.getAppId(function (id) {
port.postMessage({
action: 'register',
appId: id,
inspectedTabId: chrome.devtools.inspectedWindow.tabId
});
});
});

@ -1,8 +1,6 @@
// Service for running code in the context of the application being debugged
angular.module('panelApp').factory('appContext', function (chromeExtension, $rootScope) {
var port = chrome.extension.connect();
// Public API
// ==========
return {
@ -58,6 +56,8 @@ angular.module('panelApp').factory('appContext', function (chromeExtension, $roo
}
},
getAppId: getAppId,
getDebug: function (cb) {
chromeExtension.eval(function (window) {
return document.cookie.indexOf('__ngDebug=true') !== -1;
@ -70,58 +70,13 @@ angular.module('panelApp').factory('appContext', function (chromeExtension, $roo
chromeExtension.eval('function (window) {' +
'window.__ngDebug.log = ' + setting.toString() + ';' +
'}');
},
// Registering events
// ------------------
// TODO: depreciate this; only poll from now on?
// There are some cases where you need to gather data on a once-per-bootstrap basis, for
// instance getting the version of AngularJS
// TODO: move to chromeExtension?
watchRefresh: function (cb) {
port.postMessage({
action: 'register',
inspectedTabId: chrome.devtools.inspectedWindow.tabId
});
port.onMessage.addListener(function (msg) {
if (msg === 'refresh') {
cb();
}
});
},
// TODO: move to chromeExtension?
watchModelChange: function (cb) {
port.onMessage.addListener(function (msg) {
if (msg.action === 'modelChange') {
$rootScope.$apply(function () {
cb(msg);
});
}
});
},
watchScopeChange: function (cb) {
port.onMessage.addListener(function (msg) {
if (msg.action === 'scopeChange') {
$rootScope.$apply(function () {
cb(msg);
});
}
});
},
watchWatcherChange: function (cb) {
port.onMessage.addListener(function (msg) {
if (msg.action === 'watcherChange') {
$rootScope.$apply(function () {
cb(msg);
});
}
});
}
};
function getAppId (cb) {
chromeExtension.eval(function (window) {
return window.__ngDebug.getAppId();
}, cb);
}
});

@ -1,10 +1,12 @@
// Service for retrieving and caching application dependencies
angular.module('panelApp').factory('appDeps', function (chromeExtension, appContext) {
angular.module('panelApp').
factory('appDeps', function (chromeExtension, $rootScope) {
var _depsCache = [];
// clear cache on page refresh
appContext.watchRefresh(function () {
$rootScope.$on('refresh', function () {
_depsCache = [];
});

@ -1,11 +1,13 @@
// Service for running code in the context of the application being debugged
angular.module('panelApp').factory('appInfo', function (chromeExtension, appContext) {
angular.module('panelApp').
factory('appInfo', function (chromeExtension, $rootScope) {
var _versionCache = null,
_srcCache = null;
// clear cache on page refresh
appContext.watchRefresh(function () {
$rootScope.$on('refresh', function () {
_versionCache = null;
_srcCache = null;
});

@ -1,26 +1,47 @@
// Service for running code in the context of the application being debugged
angular.module('panelApp').factory('appModel', function (chromeExtension, appContext) {
angular.module('panelApp').
factory('appModel', function ($rootScope, chromeExtension) {
var _scopeTreeCache = {},
_scopeCache = {},
_rootScopeCache = [];
_rootScopeIdCache = [];
appContext.watchRefresh(function clearCaches () {
_scopeCache = {};
_scopeTreeCache = {};
_rootScopeCache = [];
$rootScope.$on('referesh', function clearCaches () {
emptyObject(_scopeCache);
emptyObject(_scopeTreeCache);
emptyArray(_rootScopeIdCache);
});
appContext.watchScopeChange(function (data) {
if (_rootScopeCache.indexOf(data.id) === -1) {
_rootScopeCache.push(data.id);
function emptyObject (obj) {
for (prop in obj) {
if (obj.hasOwnProperty(obj)) {
delete obj[prop];
}
}
}
function emptyArray (arr) {
arr.splice(0, arr.length);
}
$rootScope.$on('scopeChange', function (ev, data) {
if (_rootScopeIdCache.indexOf(data.id) === -1) {
_rootScopeIdCache.push(data.id);
$rootScope.$broadcast('rootScopeChange', _rootScopeIdCache);
}
_scopeTreeCache[data.id] = data.scope;
});
$rootScope.$on('scopeDeleted', function (ev, data) {
_rootScopeIdCache.splice(_rootScopeIdCache.indexOf(data.id), 1);
$rootScope.$broadcast('rootScopeChange', _rootScopeIdCache);
delete _scopeTreeCache[data.id];
});
return {
getRootScopes: function () {
return _rootScopeCache;
getRootScopeIds: function () {
return _rootScopeIdCache.slice();
},
getModel: function (id, path, callback) {

@ -1,5 +1,7 @@
// Service for retrieving and caching performance data
angular.module('panelApp').factory('appPerf', function (chromeExtension, appContext) {
angular.module('panelApp').
factory('appPerf', function (chromeExtension, $rootScope) {
var _histogramCache = [],
_watchNameToPerf = {},
@ -12,9 +14,7 @@ angular.module('panelApp').factory('appPerf', function (chromeExtension, appCont
};
// clear cache on page refresh
appContext.watchRefresh(function () {
clear();
});
$rootScope.$on('refresh', clear);
var getHistogramData = function (callback) {
chromeExtension.eval(function (window) {

@ -1,5 +1,7 @@
// abstraction layer for Chrome Extension APIs
angular.module('panelApp').value('chromeExtension', {
angular.module('panelApp').
value('chromeExtension', {
sendRequest: function (requestName, cb) {
chrome.extension.sendRequest({
script: requestName,
@ -8,8 +10,6 @@ angular.module('panelApp').value('chromeExtension', {
},
// evaluates in the context of a window
//written because I don't like the API for chrome.devtools.inspectedWindow.eval;
// passing strings instead of functions are gross.
eval: function (fn, args, cb) {
// with two args
if (!cb && typeof args === 'function') {

@ -1,16 +1,15 @@
<div bat-vertical-split ng-controller="ModelCtrl">
<div bat-vertical-left class="source-code">
<div ng-show="roots().length > 1">
<div ng-show="rootScopeIds.length > 1">
<label for="select-root">Root
<select
ng-options="root.toString() for root in roots()"
ng-model="selectedRoot"></select>
ng-options="root for root in rootScopeIds"
ng-model="selectedRootScopeId"></select>
</label>
</div>
<bat-scope-tree
class=""
val="scopeTree()"
val="scopeTree"
inspect="inspect"
select="select"
selected-scope-id="selectedScopeId"