[Edit Mode] #627 removed edit concerns from browse controllers and markup

Fixed elements not appearing in edit mode

Fixed failing tests
This commit is contained in:
Henry 2016-02-26 17:12:35 -08:00
parent a910fa8f37
commit 2cc2c6a9d3
11 changed files with 140 additions and 73 deletions

View File

@ -111,7 +111,6 @@ define([
"$scope", "$scope",
"$route", "$route",
"$location", "$location",
"$q",
"objectService", "objectService",
"navigationService", "navigationService",
"urlService", "urlService",

View File

@ -72,7 +72,7 @@
<div class="split-pane-component t-inspect pane right mobile-hide"> <div class="split-pane-component t-inspect pane right mobile-hide">
<mct-representation key="'object-inspector'" <mct-representation key="'object-inspector'"
mct-object="domainObject" mct-object="navigatedObject"
ng-model="treeModel"> ng-model="treeModel">
</mct-representation> </mct-representation>
<a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect" <a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect"

View File

@ -28,6 +28,6 @@
representation of the object as needed. representation of the object as needed.
--> -->
<mct-representation mct-object="domainObject" <mct-representation mct-object="domainObject"
key="viewRegionTemplate || 'browse-object'" key="viewObjectTemplate || 'browse-object'"
class="abs holder holder-object"> class="abs holder holder-object">
</mct-representation> </mct-representation>

View File

@ -49,7 +49,6 @@ define(
$scope, $scope,
$route, $route,
$location, $location,
$q,
objectService, objectService,
navigationService, navigationService,
urlService, urlService,

View File

@ -57,7 +57,7 @@ define(
NavigationService.prototype.setNavigation = function (value) { NavigationService.prototype.setNavigation = function (value) {
var canNavigate = true; var canNavigate = true;
if (this.navigated !== value) { if (this.navigated !== value) {
canNavigate = (this.callbacks['before'] || []) canNavigate = (this.callbacks.before || [])
.reduce(function (previous, callback) { .reduce(function (previous, callback) {
//Check whether the callback returned a value of //Check whether the callback returned a value of
// 'false' indicating that navigation should not // 'false' indicating that navigation should not
@ -67,7 +67,7 @@ define(
}, true); }, true);
if (canNavigate) { if (canNavigate) {
this.navigated = value; this.navigated = value;
this.callbacks['after'].forEach(function (callback) { (this.callbacks.after || []).forEach(function (callback) {
callback(value); callback(value);
}); });
} }

View File

@ -29,8 +29,7 @@ define(
function (BrowseController) { function (BrowseController) {
"use strict"; "use strict";
//TODO: Disabled for NEM Beta describe("The browse controller", function () {
xdescribe("The browse controller", function () {
var mockScope, var mockScope,
mockRoute, mockRoute,
mockLocation, mockLocation,
@ -214,7 +213,10 @@ define(
// prior to setting $route.current // prior to setting $route.current
mockLocation.path.andReturn("/browse/"); mockLocation.path.andReturn("/browse/");
mockNavigationService.setNavigation.andReturn(true);
// Exercise the Angular workaround // Exercise the Angular workaround
mockNavigationService.addListener.mostRecentCall.args[0]();
mockScope.$on.mostRecentCall.args[1](); mockScope.$on.mostRecentCall.args[1]();
expect(mockUnlisten).toHaveBeenCalled(); expect(mockUnlisten).toHaveBeenCalled();
@ -225,6 +227,32 @@ define(
); );
}); });
it("after successful navigation event sets the selected tree " +
"object", function () {
mockScope.navigatedObject = mockDomainObject;
mockNavigationService.setNavigation.andReturn(true);
//Simulate a change in selected tree object
mockScope.treeModel = {selectedObject: mockDomainObject};
mockScope.$watch.mostRecentCall.args[1](mockNextObject);
expect(mockScope.treeModel.selectedObject).toBe(mockNextObject);
expect(mockScope.treeModel.selectedObject).not.toBe(mockDomainObject);
});
it("after failed navigation event resets the selected tree" +
" object", function () {
mockScope.navigatedObject = mockDomainObject;
mockNavigationService.setNavigation.andReturn(false);
//Simulate a change in selected tree object
mockScope.treeModel = {selectedObject: mockDomainObject};
mockScope.$watch.mostRecentCall.args[1](mockNextObject);
expect(mockScope.treeModel.selectedObject).not.toBe(mockNextObject);
expect(mockScope.treeModel.selectedObject).toBe(mockDomainObject);
});
}); });
} }
); );

View File

@ -84,6 +84,24 @@ define(
expect(callback).not.toHaveBeenCalled(); expect(callback).not.toHaveBeenCalled();
}); });
it("adds listeners to the 'after' state by default", function(){
expect(navigationService.callbacks.after).toBeUndefined();
navigationService.addListener(function(){});
expect(navigationService.callbacks.after).toBeDefined();
expect(navigationService.callbacks.after.length).toBe(1);
});
it("allows navigationService events to be prevented", function(){
var callback = jasmine.createSpy("callback"),
navigationResult;
callback.andReturn(false);
navigationService.addListener(callback, "before");
navigationResult = navigationService.setNavigation({});
expect(callback).toHaveBeenCalled();
expect(navigationResult).toBe(false);
});
}); });
} }
); );

View File

@ -102,6 +102,11 @@ define(
scope.commit = commit; scope.commit = commit;
scope.setEditable = setEditable; scope.setEditable = setEditable;
// Clean up when the scope is destroyed
scope.$on("$destroy", function () {
self.destroy();
});
} }
// Handle a specific representation of a specific domain object // Handle a specific representation of a specific domain object
@ -119,8 +124,7 @@ define(
this.destroy(); this.destroy();
function setEditing(){ function setEditing(){
scope.viewRegionTemplate = 'edit-object'; scope.viewObjectTemplate = 'edit-object';
scope.inspectorRegionTemplate = 'inspector-edit'
} }
/** /**
@ -132,7 +136,7 @@ define(
if (statuses.indexOf('editing')!=-1){ if (statuses.indexOf('editing')!=-1){
setEditing(); setEditing();
} else { } else {
delete scope.viewRegionTemplate; delete scope.viewObjectTemplate;
} }
}); });
@ -144,7 +148,7 @@ define(
// Respond to the destruction of the current representation. // Respond to the destruction of the current representation.
EditRepresenter.prototype.destroy = function destroy() { EditRepresenter.prototype.destroy = function destroy() {
// Nothing to clean up // Nothing to clean up
this.listenHandle && this.listenHandle(); return this.listenHandle && this.listenHandle();
}; };
return EditRepresenter; return EditRepresenter;

View File

@ -22,89 +22,72 @@
/*global define,describe,it,expect,beforeEach,jasmine*/ /*global define,describe,it,expect,beforeEach,jasmine*/
define( define(
["../../src/controllers/EditController"], ["../../src/controllers/EditObjectController"],
function (EditController) { function (EditObjectController) {
"use strict"; "use strict";
describe("The Edit mode controller", function () { describe("The Edit mode controller", function () {
var mockScope, var mockScope,
mockQ,
mockNavigationService,
mockObject, mockObject,
mockType, mockType,
mockLocation,
mockStatusCapability,
mockCapabilities,
controller; controller;
// Utility function; look for a $watch on scope and fire it
function fireWatch(expr, value) {
mockScope.$watch.calls.forEach(function (call) {
if (call.args[0] === expr) {
call.args[1](value);
}
});
}
beforeEach(function () { beforeEach(function () {
mockScope = jasmine.createSpyObj( mockScope = jasmine.createSpyObj(
"$scope", "$scope",
[ "$on" ] [ "$on", "$watch" ]
);
mockQ = jasmine.createSpyObj('$q', ['when', 'all']);
mockNavigationService = jasmine.createSpyObj(
"navigationService",
[ "getNavigation", "addListener", "removeListener" ]
); );
mockObject = jasmine.createSpyObj( mockObject = jasmine.createSpyObj(
"domainObject", "domainObject",
[ "getId", "getModel", "getCapability", "hasCapability" ] [ "getId", "getModel", "getCapability", "hasCapability", "useCapability" ]
); );
mockType = jasmine.createSpyObj( mockType = jasmine.createSpyObj(
"type", "type",
[ "hasFeature" ] [ "hasFeature" ]
); );
mockStatusCapability = jasmine.createSpyObj('statusCapability',
["get"]
);
mockCapabilities = {
"type" : mockType,
"status": mockStatusCapability
};
mockLocation = jasmine.createSpyObj('$location',
["search"]
);
mockLocation.search.andReturn({"view": "fixed"});
mockNavigationService.getNavigation.andReturn(mockObject);
mockObject.getId.andReturn("test"); mockObject.getId.andReturn("test");
mockObject.getModel.andReturn({ name: "Test object" }); mockObject.getModel.andReturn({ name: "Test object" });
mockObject.getCapability.andCallFake(function (key) { mockObject.getCapability.andCallFake(function (key) {
return key === 'type' && mockType; return mockCapabilities[key];
}); });
mockType.hasFeature.andReturn(true); mockType.hasFeature.andReturn(true);
controller = new EditController( mockScope.domainObject = mockObject;
controller = new EditObjectController(
mockScope, mockScope,
mockQ, mockLocation
mockNavigationService
); );
}); });
it("exposes the currently-navigated object", function () {
expect(controller.navigatedObject()).toBeDefined();
expect(controller.navigatedObject().getId()).toEqual("test");
});
it("adds an editor capability to the navigated object", function () {
// Should provide an editor capability...
expect(controller.navigatedObject().getCapability("editor"))
.toBeDefined();
// Shouldn't have been the mock capability we provided
expect(controller.navigatedObject().getCapability("editor"))
.not.toEqual(mockType);
});
it("detaches its navigation listener when destroyed", function () {
var navCallback = mockNavigationService
.addListener.mostRecentCall.args[0];
expect(mockScope.$on).toHaveBeenCalledWith(
"$destroy",
jasmine.any(Function)
);
// Verify precondition
expect(mockNavigationService.removeListener)
.not.toHaveBeenCalled();
// Trigger destroy
mockScope.$on.mostRecentCall.args[1]();
// Listener should have been removed
expect(mockNavigationService.removeListener)
.toHaveBeenCalledWith(navCallback);
});
it("exposes a warning message for unload", function () { it("exposes a warning message for unload", function () {
var obj = controller.navigatedObject(), var obj = mockObject,
mockEditor = jasmine.createSpyObj('editor', ['dirty']); mockEditor = jasmine.createSpyObj('editor', ['dirty']);
// Normally, should be undefined // Normally, should be undefined
@ -112,14 +95,32 @@ define(
// Override the object's editor capability, make it look // Override the object's editor capability, make it look
// like there are unsaved changes. // like there are unsaved changes.
obj.getCapability = jasmine.createSpy(); mockCapabilities.editor = mockEditor;
obj.getCapability.andReturn(mockEditor);
mockEditor.dirty.andReturn(true); mockEditor.dirty.andReturn(true);
mockStatusCapability.get.andReturn(true);
// Should have some warning message here now // Should have some warning message here now
expect(controller.getUnloadWarning()).toEqual(jasmine.any(String)); expect(controller.getUnloadWarning()).toEqual(jasmine.any(String));
}); });
it("sets the active view from query parameters", function () {
var testViews = [
{ key: 'abc' },
{ key: 'def', someKey: 'some value' },
{ key: 'xyz' }
];
mockObject.useCapability.andCallFake(function (c) {
return (c === 'view') && testViews;
});
mockLocation.search.andReturn({ view: 'def' });
fireWatch('domainObject', mockObject);
expect(mockScope.representation.selected)
.toEqual(testViews[1]);
});
}); });
} }
); );

View File

@ -31,6 +31,7 @@ define(
mockScope, mockScope,
testAttrs, testAttrs,
mockEvent, mockEvent,
mockNavigationService,
directive; directive;
function fireListener(eventType, value) { function fireListener(eventType, value) {
@ -46,7 +47,8 @@ define(
mockScope = jasmine.createSpyObj("$scope", ['$eval', '$on']); mockScope = jasmine.createSpyObj("$scope", ['$eval', '$on']);
testAttrs = { mctBeforeUnload: "someExpression" }; testAttrs = { mctBeforeUnload: "someExpression" };
mockEvent = jasmine.createSpyObj("event", ["preventDefault"]); mockEvent = jasmine.createSpyObj("event", ["preventDefault"]);
directive = new MCTBeforeUnload(mockWindow); mockNavigationService = jasmine.createSpyObj("navigationService", ["addListener", "removeListener"]);
directive = new MCTBeforeUnload(mockWindow, mockNavigationService);
directive.link(mockScope, {}, testAttrs); directive.link(mockScope, {}, testAttrs);
}); });
@ -65,6 +67,10 @@ define(
); );
}); });
it("listens for navigation changes", function () {
expect(mockNavigationService.addListener).toHaveBeenCalledWith(jasmine.any(Function), "before");
});
it("listens for its scope's destroy event", function () { it("listens for its scope's destroy event", function () {
expect(mockScope.$on).toHaveBeenCalledWith( expect(mockScope.$on).toHaveBeenCalledWith(
"$destroy", "$destroy",
@ -108,9 +114,10 @@ define(
it("cleans up listeners when destroyed", function () { it("cleans up listeners when destroyed", function () {
fireListener("$destroy", mockEvent); fireListener("$destroy", mockEvent);
expect(mockWindow.onbeforeunload).toBeUndefined(); expect(mockWindow.onbeforeunload).toBeUndefined();
expect(mockNavigationService.removeListener).toHaveBeenCalled();
}); });
}); });
} }
); );

View File

@ -33,8 +33,8 @@ define(
testRepresentation, testRepresentation,
mockDomainObject, mockDomainObject,
mockPersistence, mockPersistence,
mockCapabilities,
mockStatusCapability, mockStatusCapability,
mockCapabilities,
representer; representer;
function mockPromise(value) { function mockPromise(value) {
@ -48,7 +48,7 @@ define(
beforeEach(function () { beforeEach(function () {
mockQ = { when: mockPromise }; mockQ = { when: mockPromise };
mockLog = jasmine.createSpyObj("$log", ["info", "debug"]); mockLog = jasmine.createSpyObj("$log", ["info", "debug"]);
mockScope = jasmine.createSpyObj("$scope", ["$watch"]); mockScope = jasmine.createSpyObj("$scope", ["$watch", "$on"]);
testRepresentation = { key: "test" }; testRepresentation = { key: "test" };
mockDomainObject = jasmine.createSpyObj("domainObject", [ mockDomainObject = jasmine.createSpyObj("domainObject", [
"getId", "getId",
@ -60,7 +60,7 @@ define(
mockPersistence = mockPersistence =
jasmine.createSpyObj("persistence", ["persist"]); jasmine.createSpyObj("persistence", ["persist"]);
mockStatusCapability = mockStatusCapability =
jasmine.createSpyObj("status", ["get"]); jasmine.createSpyObj("statusCapability", ["get", "listen"]);
mockStatusCapability.get.andReturn(false); mockStatusCapability.get.andReturn(false);
mockCapabilities = { mockCapabilities = {
'persistence': mockPersistence, 'persistence': mockPersistence,
@ -82,6 +82,17 @@ define(
expect(mockScope.commit).toEqual(jasmine.any(Function)); expect(mockScope.commit).toEqual(jasmine.any(Function));
}); });
it("Sets edit view template on edit mode", function () {
mockStatusCapability.listen.mostRecentCall.args[0](['editing']);
expect(mockScope.viewObjectTemplate).toEqual('edit-object');
});
it("Cleans up listeners on scope destroy", function () {
representer.listenHandle = jasmine.createSpy('listen');
mockScope.$on.mostRecentCall.args[1]();
expect(representer.listenHandle).toHaveBeenCalled();
});
it("mutates and persists upon observed changes", function () { it("mutates and persists upon observed changes", function () {
mockScope.model = { someKey: "some value" }; mockScope.model = { someKey: "some value" };
mockScope.configuration = { someConfiguration: "something" }; mockScope.configuration = { someConfiguration: "something" };
@ -112,4 +123,4 @@ define(
}); });
} }
); );