launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #06240
[Merge] lp:~rvb/maas/maas-add-node-js3 into lp:maas
Raphaël Badin has proposed merging lp:~rvb/maas/maas-add-node-js3 into lp:maas with lp:~rvb/maas/maas-add-node-js2 as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~rvb/maas/maas-add-node-js3/+merge/91440
This branch adds a Dashboard view used on the index page. The dashboard is pretty minimal right now, it simply displays the number of nodes.
- added the Dashboard view which preloads the visible nodes and the listens to the 'nodeAdded' event.
- renamed add_node to node_add (YUI module name, files, etc).
- added node.js with models for Node and NodeList. This is pretty minimal right now but it's all we need to count the nodes.
- added testing.js which contains a base class for tests that need to mockup io or cleanup event handlers. I assumes that the io provided is stored in module._io, this is not really nice but I think it's better than to modify the modules themself for the sole purpose of being able to test them. test_node_add.js has been cleaned up to take advantage of this.
--
https://code.launchpad.net/~rvb/maas/maas-add-node-js3/+merge/91440
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/maas/maas-add-node-js3 into lp:maas.
=== added file 'src/maasserver/static/js/node.js'
--- src/maasserver/static/js/node.js 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/js/node.js 2012-02-03 14:44:20 +0000
@@ -0,0 +1,43 @@
+/* Copyright 2012 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ *
+ * Node model.
+ *
+ * @module Y.mass.node
+ */
+
+YUI.add('maas.node', function(Y) {
+
+Y.log('loading mass.node');
+var module = Y.namespace('maas.node');
+
+/**
+ * A Y.Model to represent a Node.
+ *
+ */
+module.Node = Y.Base.create('nodeModel', Y.Model, [], {
+ idAttribute: 'system_id'
+}, {
+ ATTRS: {
+ system_id: {
+ },
+ hostname: {
+ },
+ status: {
+ },
+ after_commissioning_action: {
+ }
+ }
+});
+
+/**
+ * A Y.ModelList that is meant to contain instances of Y.maas.node.Node.
+ *
+ */
+module.NodeList = Y.Base.create('nodeList', Y.ModelList, [], {
+ model: module.Node
+
+});
+
+}, '0.1', {'requires': ['model', 'model-list']}
+);
=== renamed file 'src/maasserver/static/js/add_node.js' => 'src/maasserver/static/js/node_add.js'
--- src/maasserver/static/js/add_node.js 2012-02-02 16:50:18 +0000
+++ src/maasserver/static/js/node_add.js 2012-02-03 14:44:20 +0000
@@ -3,14 +3,14 @@
*
* Widget to add a Node.
*
- * @module Y.mass.add_node
+ * @module Y.mass.node_add
*/
-YUI.add('maas.add_node', function(Y) {
-
-Y.log('loading mass.add_node');
-
-var module = Y.namespace('maas.add_node');
+YUI.add('maas.node_add', function(Y) {
+
+Y.log('loading mass.node_add');
+
+var module = Y.namespace('maas.node_add');
module.NODE_ADDED_EVENT = 'nodeAdded';
@@ -21,7 +21,7 @@
AddNodeWidget.superclass.constructor.apply(this, arguments);
};
-AddNodeWidget.NAME = 'add-node-widget';
+AddNodeWidget.NAME = 'node-add-widget';
AddNodeWidget.ATTRS = {
@@ -118,10 +118,10 @@
},
/**
- * Show the spinner.
- *
- * @method showSpinner
- */
+ * Show the spinner.
+ *
+ * @method showSpinner
+ */
showSpinner: function() {
var button = this.get('srcNode').one('.add-node-button');
button.insert(this.spinnerNode, 'after');
=== added file 'src/maasserver/static/js/node_views.js'
--- src/maasserver/static/js/node_views.js 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/js/node_views.js 2012-02-03 14:44:20 +0000
@@ -0,0 +1,192 @@
+/* Copyright 2012 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ *
+ * Node model.
+ *
+ * @module Y.mass.node
+ */
+
+YUI.add('maas.node_views', function(Y) {
+
+Y.log('loading mass.node_views');
+var module = Y.namespace('maas.node_views');
+
+// Only used to mockup io in tests.
+module._io = Y;
+
+/**
+ * A base view class to display a set of Nodes (Y.maas.node.Node).
+ *
+ * It will load the list of visible nodes (in this.modelList) when rendered
+ * for the first time and be subscribed to 'nodeAdded' events published by
+ * Y.maas.node_add.AddNodeDispatcher. Changes to this.modelList will trigger
+ * re-rendering.
+ *
+ * You can provide your custom rendering method by defining a 'display'
+ * method (also, you can provide methods named 'loadNodesStarted' and
+ * 'loadNodesEnded' to customize the display during the initial loading of the
+ * visible nodes and a method named 'displayGlobalError' to display a message
+ * when errors occur during loading).
+ *
+ */
+module.NodeListLoader = Y.Base.create('nodeListLoader', Y.View, [], {
+
+ initializer: function(config) {
+ this.modelList = new Y.maas.node.NodeList();
+ this.nodes_loaded = false;
+ this.handle = this.registerAddNodeDispatcher(
+ Y.maas.node_add.AddNodeDispatcher);
+ },
+
+ destructor: function() {
+ this.handle.detach();
+ },
+
+ render: function () {
+ if (this.nodes_loaded) {
+ this.display();
+ }
+ else {
+ this.loadNodesAndRender();
+ }
+ },
+
+ registerAddNodeDispatcher: function(dispatcher) {
+ return dispatcher.on(
+ Y.maas.node_add.NODE_ADDED_EVENT,
+ function(e, node) {
+ this.modelList.add(node);
+ },
+ this);
+ },
+
+ /**
+ * Load visible Nodes (store them in this.modelList) and render this view.
+ * to be populated.
+ *
+ * @method loadNodesAndRender
+ */
+ loadNodesAndRender: function() {
+ var self = this;
+ var cfg = {
+ method: 'GET',
+ sync: false,
+ on: {
+ start: Y.bind(self.loadNodesStarted, self),
+ success: function(id, out) {
+ var node_data;
+ try {
+ node_data = JSON.parse(out.response);
+ }
+ catch(e) {
+ // Parsing error.
+ self.displayGlobalError('Unable to load nodes.');
+ }
+ self.modelList.add(node_data);
+ self.modelList.after(
+ ['add', 'remove', 'reset'], self.render, self);
+ self.nodes_loaded = true;
+ self.display();
+ },
+ failure: function(id, out) {
+ // Unexpected error.
+ self.displayGlobalError('Unable to load nodes.');
+ },
+ end: Y.bind(self.loadNodesEnded, self)
+ }
+ };
+ var request = module._io.io(
+ MAAS_config.uris.nodes_handler, cfg);
+ },
+
+ /**
+ * Function called when rendering occurs. this.modelList is guaranteed
+ * to be populated.
+ *
+ * @method display
+ */
+ display: function () {
+ },
+
+ /**
+ * Function called if an error occurs during the initial node loading.
+ * to be populated.
+ *
+ * @method display
+ */
+ displayGlobalError: function (error_message) {
+ },
+
+ /**
+ * Function called when the Node list starts loading.
+ *
+ * @method loadNodesStarted
+ */
+ loadNodesStarted: function() {
+ },
+
+ /**
+ * Function called when the Node list has loaded.
+ *
+ * @method loadNodesEnded
+ */
+ loadNodesEnded: function() {
+ }
+
+});
+
+/**
+ * A customized view based on NodeListLoader that will display a dashboard
+ * of the nodes.
+ *
+ * @method display
+ */
+module.NodesDashboard = Y.Base.create(
+ 'nodesDashboard', module.NodeListLoader, [], {
+
+ plural_template: (
+ '<h2>{nb_nodes} nodes in this cluster</h2><div id="chart" />'),
+ singular_template: (
+ '<h2>{nb_nodes} node in this cluster</h2><div id="chart" />'),
+
+ initializer: function(config) {
+ this.append = config.append;
+ // Prepare spinnerNode.
+ this.spinnerNode = Y.Node.create('<img />')
+ .set('src', MAAS_config.uris.statics + 'img/spinner.gif');
+ },
+
+ /**
+ * Display a dashboard of the nodes (right now a simple count).
+ *
+ * @method display
+ */
+ display: function () {
+ var size = this.modelList.size();
+ var template;
+ if (size === 1) {
+ template = this.singular_template;
+ }
+ else {
+ template = this.plural_template;
+ }
+ Y.one(this.container).setContent(
+ Y.Lang.sub(template, {nb_nodes: size}));
+
+ if (!this.container.inDoc()) {
+ Y.one(this.append).empty().append(this.container, 0);
+ }
+ },
+
+ loadNodesStarted: function() {
+ Y.one(this.append).insert(this.spinnerNode, 0);
+ },
+
+ loadNodesEnded: function() {
+ this.spinnerNode.remove();
+ }
+
+});
+
+}, '0.1', {'requires': ['view', 'io', 'maas.node', 'maas.node_add']}
+);
=== added file 'src/maasserver/static/js/testing/testing.js'
--- src/maasserver/static/js/testing/testing.js 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/js/testing/testing.js 2012-02-03 14:44:20 +0000
@@ -0,0 +1,76 @@
+/* Copyright 2012 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ */
+
+YUI().add('maas.testing', function(Y) {
+
+Y.log('loading mass.testing');
+
+var module = Y.namespace('maas.testing');
+
+module.TestCase = Y.Base.create('ioMockableTestCase', Y.Test.Case, [], {
+
+ /**
+ * Mock the '_io' field of the provided module. This assumes that
+ * the module has a internal reference to its io module named '_io'
+ * and that all its io is done via module._io.io(...).
+ *
+ * @method mockIO
+ * @param mock the mock object that should replace the module's io
+ * @param module the module to monkey patch
+ */
+ mockIO: function(mock, module) {
+ this.old_io = module._io;
+ this.module = module;
+ this.module._io = mock;
+ },
+
+ tearDown: function() {
+ if (Y.Lang.isValue(this.old_io)) {
+ this.module._io = this.old_io;
+ }
+ if (Y.Lang.isValue(this.handlers)) {
+ var handler;
+ while(handler=this.handlers.pop()) {
+ handler.detach();
+ }
+ }
+ },
+
+ mockSuccess: function(response, module) {
+ var mockXhr = {};
+ mockXhr.io = function(url, cfg) {
+ var out = {};
+ out.response = response;
+ cfg.on.success('4', out);
+ };
+ this.mockIO(mockXhr, module);
+ },
+
+ /**
+ * Register a method to be fired when the event 'name' is triggered on
+ * 'source'. The handle will be cleaned up when the test finishes.
+ *
+ * @method registerListener
+ * @param source the source of the event
+ * @param name the name of the event to listen to
+ * @param method the method to run
+ * @param context the context in which the method should be run
+ */
+ registerListener: function(source, name, method, context) {
+ var handle = source.on(name, method, context);
+ this.cleanupHandler(handle);
+ return handle;
+ },
+
+ cleanupHandler: function(handler) {
+ if (!Y.Lang.isValue(this.handlers)) {
+ this.handlers = [];
+ }
+ this.handlers.push(handler);
+ }
+
+});
+
+}, '0.1', {'requires': ['test', 'base']}
+);
=== modified file 'src/maasserver/static/js/testing/testrunner.js'
--- src/maasserver/static/js/testing/testrunner.js 2012-02-02 16:50:18 +0000
+++ src/maasserver/static/js/testing/testrunner.js 2012-02-03 14:44:20 +0000
@@ -1,3 +1,7 @@
+/* Copyright 2012 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ */
+
/**
* Merely loading this script into a page will cause it to look for a
* single suite using the selector span#suite. If found, the text
=== added file 'src/maasserver/static/js/tests/test_node.html'
--- src/maasserver/static/js/tests/test_node.html 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/js/tests/test_node.html 2012-02-03 14:44:20 +0000
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Test maas.node</title>
+
+ <!-- YUI and test setup -->
+ <script type="text/javascript" src="../yui/tests/yui/yui-min.js"></script>
+ <script type="text/javascript" src="../testing/testrunner.js"></script>
+ <!-- The module under test -->
+ <script type="text/javascript" src="../node.js"></script>
+ <!-- The test suite -->
+ <script type="text/javascript" src="test_node.js"></script>
+ </head>
+ <body>
+ <span id="suite">maas.node.tests</span>
+ </body>
+</html>
=== added file 'src/maasserver/static/js/tests/test_node.js'
--- src/maasserver/static/js/tests/test_node.js 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/js/tests/test_node.js 2012-02-03 14:44:20 +0000
@@ -0,0 +1,33 @@
+/* Copyright 2012 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ */
+
+YUI({ useBrowserConsole: true }).add('maas.node.tests', function(Y) {
+
+Y.log('loading mass.node.tests');
+var namespace = Y.namespace('maas.node.tests');
+
+var module = Y.maas.node;
+var suite = new Y.Test.Suite("maas.node Tests");
+
+suite.add(new Y.Test.Case({
+ name: 'test-node',
+
+ testNode: function() {
+ var node = new module.Node({'system_id': '5'});
+ Y.Assert.areSame(node.idAttribute, 'system_id');
+ Y.Assert.areSame('5', node.get('system_id'));
+ },
+
+ testNodeList: function() {
+ var node_list = new module.NodeList();
+ Y.Assert.areSame(module.Node, node_list.model);
+ }
+
+}));
+
+namespace.suite = suite;
+
+}, '0.1', {'requires': [
+ 'node-event-simulate', 'test', 'maas.node']}
+);
=== renamed file 'src/maasserver/static/js/tests/test_add_node.html' => 'src/maasserver/static/js/tests/test_node_add.html'
--- src/maasserver/static/js/tests/test_add_node.html 2012-02-02 16:50:18 +0000
+++ src/maasserver/static/js/tests/test_node_add.html 2012-02-03 14:44:20 +0000
@@ -1,8 +1,16 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
- <title>Test maas.add_node</title>
+ <title>Test maas.node_add</title>
+ <!-- YUI and test setup -->
+ <script type="text/javascript" src="../yui/tests/yui/yui-min.js"></script>
+ <script type="text/javascript" src="../testing/testrunner.js"></script>
+ <script type="text/javascript" src="../testing/testing.js"></script>
+ <!-- The module under test -->
+ <script type="text/javascript" src="../node_add.js"></script>
+ <!-- The test suite -->
+ <script type="text/javascript" src="test_node_add.js"></script>
<script type="text/javascript">
<!--
var MAAS_config = {
@@ -13,13 +21,6 @@
};
// -->
</script>
- <!-- YUI and test setup -->
- <script type="text/javascript" src="../yui/tests/yui/yui-min.js"></script>
- <script type="text/javascript" src="../testing/testrunner.js"></script>
- <!-- The module under test -->
- <script type="text/javascript" src="../add_node.js"></script>
- <!-- The test suite -->
- <script type="text/javascript" src="test_add_node.js"></script>
</head>
<body>
<script type="text/x-template" id="add-macaddress">
@@ -34,6 +35,6 @@
</p>
</script>
- <span id="suite">maas.add_node.tests</span>
+ <span id="suite">maas.node_add.tests</span>
</body>
</html>
=== renamed file 'src/maasserver/static/js/tests/test_add_node.js' => 'src/maasserver/static/js/tests/test_node_add.js'
--- src/maasserver/static/js/tests/test_add_node.js 2012-02-02 19:04:24 +0000
+++ src/maasserver/static/js/tests/test_node_add.js 2012-02-03 14:44:20 +0000
@@ -2,16 +2,16 @@
* GNU Affero General Public License version 3 (see the file LICENSE).
*/
-YUI({ useBrowserConsole: true }).add('maas.add_node.tests', function(Y) {
-
-Y.log('loading mass.add_node.tests');
-var namespace = Y.namespace('maas.add_node.tests');
-
-var module = Y.maas.add_node;
-var suite = new Y.Test.Suite("maas.add_node Tests");
-
-suite.add(new Y.Test.Case({
- name: 'test-add-node-widget-singleton',
+YUI({ useBrowserConsole: true }).add('maas.node_add.tests', function(Y) {
+
+Y.log('loading mass.node_add.tests');
+var namespace = Y.namespace('maas.node_add.tests');
+
+var module = Y.maas.node_add;
+var suite = new Y.Test.Suite("maas.node_add Tests");
+
+suite.add(new Y.maas.testing.TestCase({
+ name: 'test-node-add-widget-singleton',
setUp: function() {
// Silence io.
@@ -20,12 +20,7 @@
method: 'io',
args: [MAAS_config.uris.nodes_handler, Y.Mock.Value.Any]
});
- this.old_io = module._io;
- module._io = mockXhr;
- },
-
- tearDown: function() {
- module._io = this.old_io;
+ this.mockIO(mockXhr, module);
},
testSingletonCreation: function() {
@@ -55,27 +50,16 @@
}));
-suite.add(new Y.Test.Case({
+suite.add(new Y.maas.testing.TestCase({
name: 'test-add-node-widget-add-node',
- mockIO: function(mock) {
- this.old_io = module._io;
- module._io = mock;
- },
-
- tearDown: function() {
- if (Y.Lang.isValue(this.old_io)) {
- module._io = this.old_io;
- }
- },
-
testAddNodeAPICall: function() {
var mockXhr = Y.Mock();
Y.Mock.expect(mockXhr, {
method: 'io',
args: [MAAS_config.uris.nodes_handler, Y.Mock.Value.Any]
});
- this.mockIO(mockXhr);
+ this.mockIO(mockXhr, module);
module.showAddNodeWidget();
var overlay = module._add_node_singleton;
overlay.get('srcNode').one('#id_hostname').set('value', 'host');
@@ -87,28 +71,23 @@
testNodeidPopulation: function() {
var mockXhr = new Y.Base();
mockXhr.io = function(url, cfg) {
- cfg.on.success(
- 3,
- {response: Y.JSON.stringify({system_id: 3})});
+ cfg.on.success(3, {response: Y.JSON.stringify({system_id: 3})});
};
- this.mockIO(mockXhr);
+ this.mockIO(mockXhr, module);
module.showAddNodeWidget();
var overlay = module._add_node_singleton;
overlay.get('srcNode').one('#id_hostname').set('value', 'host');
var button = overlay.get('srcNode').one('button');
var fired = false;
- var handle = module.AddNodeDispatcher.on(
- module.NODE_ADDED_EVENT, function(e, node){
- Y.Assert.areEqual(3, node.system_id);
- fired = true;
- });
- try {
- button.simulate('click');
- }
- finally {
- handle.detach();
- }
+ this.registerListener(
+ Y.maas.node_add.AddNodeDispatcher, module.NODE_ADDED_EVENT,
+ function(e, node){
+ Y.Assert.areEqual(3, node.system_id);
+ fired = true;
+ }
+ );
+ button.simulate('click');
Y.Assert.isTrue(fired);
}
@@ -117,5 +96,5 @@
namespace.suite = suite;
}, '0.1', {'requires': [
- 'node-event-simulate', 'test', 'maas.add_node']}
+ 'node-event-simulate', 'test', 'maas.testing', 'maas.node_add']}
);
=== added file 'src/maasserver/static/js/tests/test_node_views.html'
--- src/maasserver/static/js/tests/test_node_views.html 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/js/tests/test_node_views.html 2012-02-03 14:44:20 +0000
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Test maas.node_views</title>
+
+ <!-- YUI and test setup -->
+ <script type="text/javascript" src="../yui/tests/yui/yui-min.js"></script>
+ <script type="text/javascript" src="../testing/testrunner.js"></script>
+ <script type="text/javascript" src="../testing/testing.js"></script>
+ <script type="text/javascript" src="../node.js"></script>
+ <script type="text/javascript" src="../node_add.js"></script>
+ <!-- The module under test -->
+ <script type="text/javascript" src="../node_views.js"></script>
+ <!-- The test suite -->
+ <script type="text/javascript" src="test_node_views.js"></script>
+ <script type="text/javascript">
+ <!--
+ var MAAS_config = {
+ uris: {
+ statics: '/static/',
+ nodes_handler: '/api/nodes/'
+ }
+ };
+ // -->
+ </script>
+ </head>
+ <body>
+ <span id="suite">maas.node_views.tests</span>
+ <div id="placeholder"></div>
+ </body>
+</html>
=== added file 'src/maasserver/static/js/tests/test_node_views.js'
--- src/maasserver/static/js/tests/test_node_views.js 1970-01-01 00:00:00 +0000
+++ src/maasserver/static/js/tests/test_node_views.js 2012-02-03 14:44:20 +0000
@@ -0,0 +1,111 @@
+/* Copyright 2012 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ */
+
+YUI({ useBrowserConsole: true }).add('maas.node_views.tests', function(Y) {
+
+Y.log('loading mass.node_views.tests');
+var namespace = Y.namespace('maas.node_views.tests');
+
+var module = Y.maas.node_views;
+var suite = new Y.Test.Suite("maas.node_views Tests");
+
+TestCase = Y.Base.create('viewDestroyer', Y.maas.testing.TestCase, [], {
+
+ tearDown: function() {
+ if (Y.Lang.isValue(this.view)) {
+ this.view.destroy();
+ }
+ }
+});
+
+suite.add(new TestCase({
+ name: 'test-node-views-NodeListLoader',
+
+ testInitialization: function() {
+ var base_view = new Y.maas.node_views.NodeListLoader();
+ Y.Assert.areEqual('nodeList', base_view.modelList.name);
+ Y.Assert.isFalse(base_view.nodes_loaded);
+ },
+
+ testRenderCallsLoad: function() {
+ // The initial call to .render() triggers the loading of the
+ // nodes.
+ var mockXhr = Y.Mock();
+ Y.Mock.expect(mockXhr, {
+ method: 'io',
+ args: [MAAS_config.uris.nodes_handler, Y.Mock.Value.Any]
+ });
+ this.mockIO(mockXhr, module);
+
+ var base_view = new Y.maas.node_views.NodeListLoader();
+ base_view.render();
+ Y.Mock.verify(mockXhr);
+ },
+
+ testDispatcherRegistered: function() {
+ // The view listens to Y.maas.node_add.AddNodeDispatcher and
+ // adds the published nodes to its internal this.modelList.
+ var base_view = new Y.maas.node_views.NodeListLoader();
+ Y.maas.node_add.AddNodeDispatcher.fire(
+ Y.maas.node_add.NODE_ADDED_EVENT, {},
+ {system_id: '4', hostname: 'dan'});
+ Y.Assert.areEqual(1, base_view.modelList.size());
+ Y.Assert.areEqual('dan', base_view.modelList.item(0).get('hostname'));
+ },
+
+ testLoadNodes: function() {
+ var response = Y.JSON.stringify([
+ {system_id: '3', hostname: 'dan'},
+ {system_id: '4', hostname: 'dee'}
+ ]);
+ this.mockSuccess(response, module);
+ var base_view = new Y.maas.node_views.NodeListLoader();
+ base_view.render();
+ Y.Assert.areEqual(2, base_view.modelList.size());
+ Y.Assert.areEqual('dan', base_view.modelList.item(0).get('hostname'));
+ Y.Assert.areEqual('dee', base_view.modelList.item(1).get('hostname'));
+ }
+
+}));
+
+suite.add(new TestCase({
+ name: 'test-node-views-NodeDashBoard',
+
+ testDisplay: function() {
+ var response = Y.JSON.stringify([
+ {system_id: '3', hostname: 'dan'},
+ {system_id: '4', hostname: 'dee'}
+ ]);
+ this.mockSuccess(response, module);
+ this.view = new Y.maas.node_views.NodesDashboard(
+ {append: '#placeholder'});
+ this.view.render();
+ Y.Assert.areEqual(
+ '2 nodes in this cluster',
+ Y.one('#placeholder').get('text'));
+ },
+
+ testDisplayUpdate: function() {
+ // The display is updated when new nodes are added.
+ this.mockSuccess(Y.JSON.stringify([]), module);
+ this.view = new Y.maas.node_views.NodesDashboard(
+ {append: '#placeholder'});
+ this.view.render();
+ Y.maas.node_add.AddNodeDispatcher.fire(
+ Y.maas.node_add.NODE_ADDED_EVENT, {},
+ {system_id: '4', hostname: 'dan'});
+ Y.Assert.areEqual(1, this.view.modelList.size());
+ Y.Assert.areEqual(
+ '1 node in this cluster',
+ Y.one('#placeholder').get('text'));
+ }
+
+}));
+
+
+namespace.suite = suite;
+
+}, '0.1', {'requires': [
+ 'node-event-simulate', 'test', 'maas.testing', 'maas.node_views']}
+);
=== modified file 'src/maasserver/templates/maasserver/index.html'
--- src/maasserver/templates/maasserver/index.html 2012-02-02 09:06:18 +0000
+++ src/maasserver/templates/maasserver/index.html 2012-02-03 14:44:20 +0000
@@ -8,9 +8,24 @@
{% block head %}
<script type="text/javascript">
<!--
- YUI().use('maas.add_node', function (Y) {
+ YUI().use('maas.node_add', 'maas.node','maas.node_views', function (Y) {
Y.on('load', function() {
- Y.one('#addnode').on('click', Y.maas.add_node.showAddNodeWidget);
+ // Create Dashboard view.
+ var view_container = Y.Node.create('<div />')
+ .set('id', 'dashboard');
+ Y.one('#content').append(view_container);
+ var view = new Y.maas.node_views.NodesDashboard(
+ {'append': '#dashboard'});
+ view.render(view_container);
+ // Create 'Add Node' link.
+ var add_node_link = Y.Node.create('<a />')
+ .set('id', 'addnode')
+ .set('text', "Add Node")
+ .set('href', '#');
+ Y.one('#content').append(add_node_link);
+ add_node_link.on('click', Y.maas.node_add.showAddNodeWidget);
+ // Wire up the view to the 'nodeAdded' event.
+ Y.one('#addnode').on('click', Y.maas.node_add.showAddNodeWidget);
});
});
// -->
@@ -18,9 +33,4 @@
{% endblock %}
{% block content %}
- <h2>
- {{ node_list|length }} node{{ node_list|length|pluralize }} in this cluster
- </h2>
-
- <a id="addnode" href="#">Add Node</a>
{% endblock %}
=== modified file 'src/maasserver/templates/maasserver/js-conf.html'
--- src/maasserver/templates/maasserver/js-conf.html 2012-02-02 09:19:28 +0000
+++ src/maasserver/templates/maasserver/js-conf.html 2012-02-03 14:44:20 +0000
@@ -17,5 +17,6 @@
<script
src="{{ STATIC_URL }}js/yui/{{ YUI_VERSION }}/yui-base/yui-base-min.js">
</script>
-<script src="{{ STATIC_URL }}js/add_node.js">
-</script>
+<script src="{{ STATIC_URL }}js/node_add.js"></script>
+<script src="{{ STATIC_URL }}js/node.js"></script>
+<script src="{{ STATIC_URL }}js/node_views.js"></script>
Follow ups