Author: evan
Date: Thu Mar 19 22:17:49 2009
New Revision: 756236

URL: http://svn.apache.org/viewvc?rev=756236&view=rev
Log:
Patch for SHINDIG-988

Added:
    
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/
    
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/index.html
    
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/datatest.js
    
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/index.html
    
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/domutil.js
    
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/index.html
Removed:
    
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/ost_test.html
    
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.js
    
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.xml
    
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/testadapter.js
Modified:
    incubator/shindig/trunk/features/pom.xml
    
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js
    
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/data.js
    
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js
    
incubator/shindig/trunk/features/src/main/javascript/features/xmlutil/xmlutil.js
    
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/datacontexttest.js
    
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/template_test.js
    
incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java

Modified: incubator/shindig/trunk/features/pom.xml
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/pom.xml?rev=756236&r1=756235&r2=756236&view=diff
==============================================================================
--- incubator/shindig/trunk/features/pom.xml (original)
+++ incubator/shindig/trunk/features/pom.xml Thu Mar 19 22:17:49 2009
@@ -81,6 +81,8 @@
                 <source>setprefs/setprefs.js</source>
                 <source>views/views.js</source>
                 <source>opensocial-data-context/datacontext.js</source>
+                <source>xmlutil/xmlutil.js</source>
+                <source>opensocial-data/data.js</source>
                 <source>opensocial-reference/opensocial.js</source>
                 <source>opensocial-reference/activity.js</source>
                 <source>opensocial-reference/address.js</source>

Modified: 
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js?rev=756236&r1=756235&r2=756236&view=diff
==============================================================================
--- 
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js
 (original)
+++ 
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js
 Thu Mar 19 22:17:49 2009
@@ -36,6 +36,81 @@
   var dataSets = {};
 
   /**
+   * Puts a data set into the global DataContext object. Fires listeners
+   * if they are satisfied by the associated key being inserted.
+   *
+   * Note that if this is passed a ResponseItem object, it will crack it open
+   * and extract the JSON payload of the wrapped API Object. This includes
+   * iterating over an array of API objects and extracting their JSON into a
+   * simple array structure.
+   *
+   * @param {string} key The key to associate with this object.
+   * @param {ResponseItem|Object} obj The data object.
+   * @param {boolean} opt_fireListeners Default true.
+   */
+  var putDataSet = function(key, obj, opt_fireListeners) {
+    var data = obj;
+    if (typeof data === 'undefined' || data === null) {
+      return;
+    }
+  
+    // NOTE: This is ugly, but required since we need to get access
+    // to the JSON/Array payload of API responses.
+    // This will crack the actual API objects and extract their JSON payloads.
+    // TODO: this code block is not described by the spec, and should be 
removed.
+    // Developers using ResponseItems should call getData() themselves.
+    if (data.getData) {
+     data = data.getData();
+     if (data.array_) {
+       var out = [];
+       for (var i = 0; i < data.array_.length; i++) {
+         out.push(data.array_[i].fields_);
+       }
+       data = out;
+     } else {
+       data = data.fields_ || data;
+     }
+    }
+  
+    dataSets[key] = data;
+    if (!(opt_fireListeners === false)) {
+      fireCallbacks(key);
+    }
+  };
+  
+  /**
+   * Registers a callback listener for a given set of keys.
+   * @param {string|Array.<string>} keys Key or set of keys to listen on.
+   * @param {Function(Array.<string>)} callback Function to call when a
+   * listener is fired.
+   * @param {booelan} oneTimeListener Remove this listener after first 
callback?
+   */
+  var registerListener = function(keys, callback, oneTimeListener) {
+    var oneTime = !!oneTimeListener;
+    var listener = {keys : {}, callback : callback, oneTime: oneTime};
+
+    if (typeof keys === 'string') {
+      listener.keys[keys] = true;
+      if (keys != '*') {
+        keys = [ keys ];
+      }
+    } else {
+      for (var i = 0; i < keys.length; i++) {
+        listener.keys[keys[i]] = true;
+      }
+    }
+    
+    listeners.push(listener);
+  
+    // Check to see if this one should fire immediately.
+    if (keys !== '*' && isDataReady(listener.keys)) {
+      window.setTimeout(function() {
+        maybeFireListener(listener, keys);
+      }, 1);
+    }
+  };
+  
+  /**
    * Checks if the data for a map of keys is available.
    * @param {Object<string, ?>} An map of keys to check.
    * @return {boolean} Data for all the keys is present.
@@ -53,7 +128,6 @@
     return true;
   };
     
-    
   /**
    * Fires a listener for a key, but only if the data is ready for other
    * keys this listener is bound to.
@@ -62,12 +136,13 @@
    */
   var maybeFireListener = function(listener, key) {
     if (isDataReady(listener.keys)) {
-      if (listener.callback(key) == 
opensocial.data.DataContext.REMOVE_LISTENER) {
+      listener.callback(key);
+      if (listener.oneTime) {
         removeListener(listener);
       }
     }
-  };
-    
+  };    
+  
   /**
    * Removes a listener from the list.
    * @param {Object} listener The listener to remove.
@@ -96,7 +171,7 @@
       for (var j = 0; j < keys.length; j++) {
         var key = keys[j];
         if (listener.keys[key] || listener.keys['*']) {
-          maybeFireListener(listener, key);
+          maybeFireListener(listener, keys);
           break;
         }
       }
@@ -106,44 +181,43 @@
 
   return {
     
-    REMOVE_LISTENER : "remove",
-    
     /**
-     * Map of existing data.  This is used externally by both the
+     * Returns a map of existing data. This is used externally by both the
      * opensocial-data and opensocial-templates feature, hence is
-     * not hidden.
+     * not hidden, despite not being part of the spec.
+     * @return {Object} A map of current data sets.
+     * TODO: Add to the spec API?
      */
-    dataSets : dataSets,
-    
+    getData : function() {
+      var data = {};
+      for (var key in dataSets) {
+        if (dataSets.hasOwnProperty(key)) {
+          data[key] = dataSets[key];
+        }
+      }
+      return data;
+    },
     
     /**
      * Registers a callback listener for a given set of keys.
      * @param {string|Array.<string>} keys Key or set of keys to listen on.
-     * @param {Function(string)} callback Function to call when a listener is 
fired.
-     * TODO: Should return a value that can later be used to return
-     *     a value.
+     * @param {Function(Array.<string>)} callback Function to call when a 
+     * listener is fired.
      */
     registerListener : function(keys, callback) {
-      var listener = {keys : {}, callback : callback};
-
-      if (typeof keys === 'string') {
-        listener.keys[keys] = true;
-      } else {
-        for (var i = 0; i < keys.length; i++) {
-          listener.keys[keys[i]] = true;
-        }
-      }
-      
-      listeners.push(listener);
-    
-      // Check to see if this one should fire immediately.
-      if (keys !== '*' && isDataReady(listener.keys)) {
-        window.setTimeout(function() {
-          maybeFireListener(listener, keys);
-        }, 1);
-      }
+      registerListener(keys, callback, false);
+    },
+        
+    /**
+     * Private version of registerListener which allows one-time listeners to
+     * be registered. Not part of the spec. Exposed because needed by 
+     * opensocial-templates.
+     * @param {string|Array.<string>} keys Key or set of keys to listen on.
+     * @param {Function(Array.<string>)} callback Function to call when a 
+     */
+    registerOneTimeListener_ : function(keys, callback) {
+      registerListener(keys, callback, true);
     },
-    
     
     /**
      * Retrieve a data set for a given key.
@@ -153,49 +227,16 @@
     getDataSet : function(key) {
       return dataSets[key];
     },
-    
-    
+        
     /**
      * Puts a data set into the global DataContext object. Fires listeners
      * if they are satisfied by the associated key being inserted.
      *
-     * Note that if this is passed a ResponseItem object, it will crack it open
-     * and extract the JSON payload of the wrapped API Object. This includes
-     * iterating over an array of API objects and extracting their JSON into a
-     * simple array structure.
-     *
      * @param {string} key The key to associate with this object.
      * @param {ResponseItem|Object} obj The data object.
-     * @param {boolean} opt_fireListeners Default true.
      */
-    putDataSet : function(key, obj, opt_fireListeners) {
-      var data = obj;
-      if (typeof data === 'undefined' || data === null) {
-        return;
-      }
-    
-      // NOTE: This is ugly, but required since we need to get access
-      // to the JSON/Array payload of API responses.
-      // This will crack the actual API objects and extract their JSON 
payloads.
-      // TODO: this code block is not described by the spec, and should be 
removed.
-      // Developers using ResponseItems should call getData() themselves.
-      if (data.getData) {
-       data = data.getData();
-       if (data.array_) {
-         var out = [];
-         for (var i = 0; i < data.array_.length; i++) {
-           out.push(data.array_[i].fields_);
-         }
-         data = out;
-       } else {
-         data = data.fields_ || data;
-       }
-      }
-    
-      dataSets[key] = data;
-      if (!(opt_fireListeners === false)) {
-        fireCallbacks(key);
-      }
+    putDataSet : function(key, obj) {
+      putDataSet(key, obj, true)
     }, 
     
     /**
@@ -208,7 +249,7 @@
       var keys = [];
       for (var key in dataSets) {
         keys.push(key);
-        this.putDataSet(key, dataSets[key], false);
+        putDataSet(key, dataSets[key], false);
       }
       fireCallbacks(keys);
     }

Modified: 
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/data.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/data.js?rev=756236&r1=756235&r2=756236&view=diff
==============================================================================
--- 
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/data.js
 (original)
+++ 
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/data.js
 Thu Mar 19 22:17:49 2009
@@ -213,7 +213,7 @@
  */
 opensocial.data.DataContext.evalExpression = function(expr) {
   return (new Function("context", "with (context) return " + expr))
-      (opensocial.data.DataContext.dataSets);
+      (opensocial.data.DataContext.getData());
 };
 
 

Modified: 
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js?rev=756236&r1=756235&r2=756236&view=diff
==============================================================================
--- 
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js
 (original)
+++ 
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js
 Thu Mar 19 22:17:49 2009
@@ -209,7 +209,7 @@
   if (!os.Container.defaultContext) {
     if ((window['gadgets'] && gadgets.util.hasFeature('opensocial-data')) ||
         (opensocial.data.DataContext)) {
-      os.Container.defaultContext = 
os.createContext(opensocial.data.DataContext.dataSets);
+      os.Container.defaultContext = 
os.createContext(opensocial.data.DataContext.getData());
     } else {
       os.Container.defaultContext = os.createContext({});
     }
@@ -254,9 +254,13 @@
       if (requiredData) {
         // This template will render when the specified data is available.
         var keys = requiredData.split(/[\, ]+/);
-        opensocial.data.DataContext.registerListener(keys,
-            os.Container.createRenderClosure(template, el, null,
-                os.Container.getDefaultContext()));
+        var callback = os.Container.createRenderClosure(template, el, null,
+            os.Container.getDefaultContext());
+        if ("true".equalsIgnoreCase(node.getAttribute("autoUpdate"))) {
+          opensocial.data.DataContext.registerListener(keys, callback);
+        } else {
+          opensocial.data.DataContext.registerOneTimeListener_(keys, callback);
+        }
       } else {
         template.renderInto(el, null, context);
       }
@@ -280,7 +284,6 @@
     opt_context) {
  var closure = function() {
    template.renderInto(element, opt_data, opt_context);
-   return opensocial.data.DataContext.REMOVE_LISTENER;
  };
  return closure;
 };

Modified: 
incubator/shindig/trunk/features/src/main/javascript/features/xmlutil/xmlutil.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/xmlutil/xmlutil.js?rev=756236&r1=756235&r2=756236&view=diff
==============================================================================
--- 
incubator/shindig/trunk/features/src/main/javascript/features/xmlutil/xmlutil.js
 (original)
+++ 
incubator/shindig/trunk/features/src/main/javascript/features/xmlutil/xmlutil.js
 Thu Mar 19 22:17:49 2009
@@ -40,7 +40,7 @@
       throw doc.firstChild.firstChild.nodeValue;
     }
     return doc;
-  } else {
+  } else if (typeof(ActiveXObject) != "undefined") {
     var doc = new ActiveXObject("MSXML2.DomDocument");
     doc.validateOnParse = false;
     doc.loadXML(str);
@@ -49,6 +49,7 @@
     }
     return doc;
   }
+  throw "No XML parser found in this browser.";
 };
 
 

Modified: 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/datacontexttest.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/datacontexttest.js?rev=756236&r1=756235&r2=756236&view=diff
==============================================================================
--- 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/datacontexttest.js
 (original)
+++ 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/datacontexttest.js
 Thu Mar 19 22:17:49 2009
@@ -55,7 +55,6 @@
   this.assertEquals('value', context.getDataSet('key'));
 };
 
-
 /**
  * Test registerListener()
  */
@@ -75,15 +74,13 @@
   this.assertNull(listenerCalledWithKey);
 
   context.putDataSet('key', 2);
-  this.assertEquals('key', listenerCalledWithKey);
+  this.assertEquals('key', listenerCalledWithKey[0]);
 
   listenerCalledWithKey = null;
   context.putDataSet('key', 3);
-  this.assertEquals('key', listenerCalledWithKey);
+  this.assertEquals('key', listenerCalledWithKey[0]);
 }
 
-
-
 /**
  * Test registerListener()
  */
@@ -96,20 +93,19 @@
     that.assertNotNull(key);
   };
   
-  context.registerListener(['one', 'two'], listener);
+  context.registerListener(['aone', 'atwo'], listener);
   this.assertNull(listenerCalledWithKey);
-  
-  context.putDataSet('one', 1);
+
+  context.putDataSet('aone', 1);
   this.assertNull(listenerCalledWithKey);
 
-  context.putDataSet('two', 2);
-  this.assertEquals('two', listenerCalledWithKey);
+  context.putDataSet('atwo', 2);
+  this.assertEquals('atwo', listenerCalledWithKey[0]);
 
-  context.putDataSet('one', 3);
-  this.assertEquals('one', listenerCalledWithKey);
+  context.putDataSet('aone', 3);
+  this.assertEquals('aone', listenerCalledWithKey[0]);
 }
 
-
 /**
  * Test registerListener() with '*'
  */
@@ -126,8 +122,63 @@
   this.assertNull(listenerCalledWithKey);
   
   context.putDataSet('one', 1);
-  this.assertEquals('one', listenerCalledWithKey);
+  this.assertEquals('one', listenerCalledWithKey[0]);
 
   context.putDataSet('two', 2);
-  this.assertEquals('two', listenerCalledWithKey);
+  this.assertEquals('two', listenerCalledWithKey[0]);
+}
+
+/**
+ * Test getData()
+ */
+DataContextTest.prototype.testGetData = function() {
+  var context = opensocial.data.getDataContext();
+  context.putDataSet('key', 'value');
+  this.assertEquals('value', context.getData()['key']);
+  context.putDataSet('key', 'value2');
+  this.assertEquals('value2', context.getData()['key']);
+  
+  // Test that altering the result of getData doesn't change the context
+  var data = context.getData();
+  data['key'] = 'ball';
+  this.assertEquals('value2', context.getDataSet('key'));
+}
+
+/**
+ * Test putDataSets()
+ */
+DataContextTest.prototype.testPutDataSets = function() {
+  var context = opensocial.data.getDataContext();
+  var counter = 0;
+  var passedKeys = null;
+  var listener = function(keys) {
+    counter++;
+    passedKeys = keys;
+  }
+  context.registerListener(['sets1', 'sets2'], listener);
+  context.putDataSets({ sets1: 'a', sets2: 'b' });
+  this.assertEquals('a', context.getDataSet('sets1'));
+  this.assertEquals('b', context.getDataSet('sets2'));
+  
+  // Test that listener was only called once.
+  this.assertEquals(1, counter);
+  
+  // Test that listener was passed both keys.
+  this.assertEquals(2, passedKeys.length);
+}
+
+/**
+ * Test registerOneTimeListener_()
+ */
+DataContextTest.prototype.testOneTimeListener = function() {
+  var context = opensocial.data.getDataContext();
+  var counter = 0;
+  var listener = function(keys) {
+    counter++;
+  }
+  context.registerOneTimeListener_('oneTime', listener);
+  context.putDataSet('oneTime', 'foo');
+  this.assertEquals(1, counter);
+  context.putDataSet('oneTime', 'bar');
+  this.assertEquals(1, counter);
 }
\ No newline at end of file

Added: 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/index.html
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/index.html?rev=756236&view=auto
==============================================================================
--- 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/index.html
 (added)
+++ 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data-context/index.html
 Thu Mar 19 22:17:49 2009
@@ -0,0 +1,68 @@
+<html>
+<head>
+  <title>Opensocial Data Context Tests</title>
+  <script>
+    function TestCase() {};
+    Function.prototype.inherits = function() {};
+  </script>
+  <script 
src="../../../../main/javascript/features/opensocial-data-context/datacontext.js"></script>
  
+  <script src="datacontexttest.js"></script>
+  <script type="text/javascript">
+      DataContextTest.prototype.assertNotNull = function(a) {
+        if (a === null) {
+          throw("Null: " + a);
+        }
+      };
+      DataContextTest.prototype.assertNull = function(a) {
+        if (a !== null) {
+          throw("Not null: " + a);
+        }
+      };
+      DataContextTest.prototype.assertEquals = function(a, b) {
+        if (a !== b) {
+          throw("Not equal: " + typeof(a) + "[" + a + "] and " + typeof(b) + 
"[" + b + "]");
+        }
+      };
+      
+      function exposeTestFunctionNames(obj) {
+        var testSource = obj ? obj.prototype : 
+            (typeof RuntimeObject != 'undefined' ? RuntimeObject('test' + '*') 
: self);
+        var testFunctionNames = [];
+        for (var i in testSource) {
+          if (i.substring(0, 4) == 'test' && typeof(testSource[i]) == 
'function')
+           testFunctionNames.push(i);
+        }
+        return testFunctionNames;
+      }
+
+      function runAllTests() {
+        var log = function(msg, forcePage) {
+          if (window.console && !forcePage) {
+            console.log(msg);
+            return;
+          } 
+          var div = document.createElement("div");
+          div.appendChild(document.createTextNode(msg));
+          document.body.appendChild(div); 
+        };
+        var obj = new DataContextTest();
+        var tests = exposeTestFunctionNames(DataContextTest);
+        var failed = 0;
+        for (var i = 0; i < tests.length; i++) {
+          log(tests[i]);
+          try {
+            obj[tests[i]]();
+            log("OK");
+          } catch (e) {
+            log("FAIL: " + e);
+            failed++;
+          }
+        }
+        log("All finished. " + i + " run. " + failed + " failed.", true);
+      }
+    </script>  
+</head>
+<body>
+    <input type="button" onclick="runAllTests()" value="Run tests"/>
+</body>
+</html>
\ No newline at end of file

Added: 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/datatest.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/datatest.js?rev=756236&view=auto
==============================================================================
--- 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/datatest.js
 (added)
+++ 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/datatest.js
 Thu Mar 19 22:17:49 2009
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+
+/**
+ * @fileoverview
+ *
+ * Unittests for the opensocial-data-context feature.
+ */
+function DataTest(name) {
+  TestCase.call(this, name);
+}
+
+DataTest.inherits(TestCase);
+
+DataTest.prototype.setUp = function() {
+};
+
+DataTest.prototype.testParseExpression = function() {
+  var expressions = [
+    [ "Hello world", null ],
+    [ "${foo}", "(foo)" ],
+    [ "Hello ${foo} world", "'Hello '+(foo)+' world'" ],
+    [ "${foo} ${bar}", "(foo)+' '+(bar)" ]
+  ];
+  for (var i = 0; i < expressions.length; i++) {
+    this.assertEquals(
+        expressions[i][1],
+        opensocial.data.parseExpression_(expressions[i][0])
+    );
+  }
+};
+
+DataTest.prototype.testEvalExpression = function() {
+  var data = {
+    'foo': 'Hello',
+    'bar': 'World'
+  };
+  data.name = {
+    'first': 'John',
+    'last': 'Doe'
+  };
+  opensocial.data.DataContext.putDataSet("test", data);
+  var expressions = [
+    [ opensocial.data.parseExpression_("Test: ${test.foo}"), "Test: Hello" ],
+    [ opensocial.data.parseExpression_("${test.foo} ${test.bar}!"), "Hello 
World!" ],
+    [ opensocial.data.parseExpression_("${test.name.first} 
${test.name.last}"), "John Doe" ]
+  ];
+  for (var i = 0; i < expressions.length; i++) {
+    this.assertEquals(
+        expressions[i][1],
+        opensocial.data.DataContext.evalExpression(expressions[i][0])
+    );
+  }
+};
+
+/**
+ * Unit test to test data result handlers.
+ */
+DataTest.prototype.testPutDataSet = function() {
+  var key = 'test1';
+  var value = 'foo';
+  opensocial.data.DataContext.putDataSet(key, value);
+  this.assertEquals(value, opensocial.data.DataContext.getDataSet(key));
+};
+
+function registerNS(prefix) {
+  opensocial.xmlutil.NSMAP[prefix] = "#" + prefix;
+};
+
+/**
+ * Unit test to check full functionality of a request handler.
+ */
+DataTest.prototype.testRequestHandler = function() {
+  registerNS("test");
+  var results = {};
+  
+  opensocial.data.registerRequestHandler('test:request', function(descriptor) {
+    results[descriptor.key] = descriptor.getAttribute('data');
+  });
+  
+  var xmlData =
+      '<test:request key="first" data="testData"/>' +
+      '<test:request key="second" data="${foo}"/>';
+  
+  try {
+    opensocial.data.loadRequests(xmlData);
+  } catch (e) {
+    // TODO: This test breaks because Mavan's JSUnit doesn't implement neither 
+    // IE's nor FF's XML parsing interface.
+    return;
+  }
+
+  this.assertNotNull(opensocial.data.requests_['first']);
+  this.assertNotNull(opensocial.data.requests_['second']);
+
+  opensocial.data.DataContext.putDataSet("foo", "bar");
+  opensocial.data.executeRequests();
+
+  this.assertEquals('testData', results['first']);
+  this.assertEquals('bar', results['second']);
+};
+
+/**
+ * Unit test to test listener functionality when a data key is put.
+ */
+DataTest.prototype.testListener = function() {
+  var fired = false;
+  opensocial.data.DataContext.registerListener('testKey', function() {
+    fired = true;
+  });
+  opensocial.data.DataContext.putDataSet('testKey', {});
+  this.assertEquals(true, fired);
+}
+

Added: 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/index.html
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/index.html?rev=756236&view=auto
==============================================================================
--- 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/index.html
 (added)
+++ 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-data/index.html
 Thu Mar 19 22:17:49 2009
@@ -0,0 +1,70 @@
+<html>
+<head>
+  <title>Opensocial Data Tests</title>
+  <script>
+    function TestCase() {};
+    Function.prototype.inherits = function() {};
+  </script>
+  <script 
src="../../../../main/javascript/features/opensocial-data-context/datacontext.js"></script>
+  <script 
src="../../../../main/javascript/features/xmlutil/xmlutil.js"></script>
+  <script 
src="../../../../main/javascript/features/opensocial-data/data.js"></script>  
+  <script src="datatest.js"></script>
+  <script type="text/javascript">
+      DataTest.prototype.assertNotNull = function(a) {
+        if (a === null) {
+          throw("Null: " + a);
+        }
+      };
+      DataTest.prototype.assertNull = function(a) {
+        if (a !== null) {
+          throw("Not null: " + a);
+        }
+      };
+      DataTest.prototype.assertEquals = function(a, b) {
+        if (a !== b) {
+          throw("Not equal: " + typeof(a) + "[" + a + "] and " + typeof(b) + 
"[" + b + "]");
+        }
+      };
+      
+      function exposeTestFunctionNames(obj) {
+        var testSource = obj ? obj.prototype : 
+            (typeof RuntimeObject != 'undefined' ? RuntimeObject('test' + '*') 
: self);
+        var testFunctionNames = [];
+        for (var i in testSource) {
+          if (i.substring(0, 4) == 'test' && typeof(testSource[i]) == 
'function')
+           testFunctionNames.push(i);
+        }
+        return testFunctionNames;
+      }
+
+      function runAllTests() {
+        var log = function(msg, forcePage) {
+          if (window.console && !forcePage) {
+            console.log(msg);
+            return;
+          } 
+          var div = document.createElement("div");
+          div.appendChild(document.createTextNode(msg));
+          document.body.appendChild(div); 
+        };
+        var obj = new DataTest();
+        var tests = exposeTestFunctionNames(DataTest);
+        var failed = 0;
+        for (var i = 0; i < tests.length; i++) {
+          log(tests[i]);
+          try {
+            obj[tests[i]]();
+            log("OK");
+          } catch (e) {
+            log("FAIL: " + e);
+            failed++;
+          }
+        }
+        log("All finished. " + i + " run. " + failed + " failed.", true);
+      }
+    </script>  
+</head>
+<body>
+    <input type="button" onclick="runAllTests()" value="Run tests"/>
+</body>
+</html>
\ No newline at end of file

Added: 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/domutil.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/domutil.js?rev=756236&view=auto
==============================================================================
--- 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/domutil.js
 (added)
+++ 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/domutil.js
 Thu Mar 19 22:17:49 2009
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+// A pseudo namespace.
+var domutil = {};
+
+/**
+ * Helper functions adapted from Selenium code.
+ *
+ * Determines if the specified element is visible. An element can be rendered
+ * invisible by setting the CSS "visibility" property to "hidden", or the
+ * "display" property to "none", either for the element itself or one if its
+ * ancestors. This method will fail if the element is not present.
+ * @param {Node} node The node to check.
+ * @return {boolean} Whether the node is visible.
+ */
+domutil.isVisible = function(node) {
+  // This test is necessary because getComputedStyle returns nothing
+  // for WebKit-based browsers if node not in document. See comment below.
+  if (node.style.display == 'none' || node.style.visibility == 'hidden') {
+    return false;
+  }
+  var visibility = this.findEffectiveStyleProperty(node, 'visibility');
+  var display = this.findEffectiveStyleProperty(node, 'display');
+  return visibility != 'hidden' && display != 'none';
+};
+
+
+/**
+ * Returns the value of the effective style specified by {...@code property}.
+ * @param {Node} element The node to query.
+ * @param {string} property The name of a style that is of interest.
+ * @return {string} The value of style {...@code property}.
+ */
+domutil.findEffectiveStyleProperty = function(element, property) {
+  var effectiveStyle = this.findEffectiveStyle(element);
+  var propertyValue = effectiveStyle[property];
+  if (propertyValue == 'inherit' && element.parentNode.style) {
+    return this.findEffectiveStyleProperty(element.parentNode, property);
+  }
+  return propertyValue;
+};
+
+
+/**
+ * Returns the effective style object.
+ * @param {Node} element The node to query.
+ * @return {CSSStyleDeclaration|undefined} The style object.
+ */
+domutil.findEffectiveStyle = function(element) {
+  if (!element.style) {
+    return undefined; // not a styled element
+  }
+  if (window.getComputedStyle) {
+    // DOM-Level-2-CSS
+    // WebKit-based browsers (Safari included) return nothing if the element
+    // is not a descendent of document ...
+    return window.getComputedStyle(element, null);
+  }
+  if (element.currentStyle) {
+    // non-standard IE alternative
+    return element.currentStyle;
+    // TODO: this won't really work in a general sense, as
+    //   currentStyle is not identical to getComputedStyle()
+    //   ... but it's good enough for 'visibility'
+  }
+  throw new Error('cannot determine effective stylesheet in this browser');
+};
+
+
+/**
+ * Returns the text content of the current node, without markup and invisible
+ * symbols. New lines are stripped and whitespace is collapsed,
+ * such that each character would be visible.
+ *
+ * @param {Node} node The node from which we are getting content.
+ * @return {string} The text content.
+ */
+domutil.getVisibleText = function(node) {
+  var textContent;
+  // NOTE(kjin): IE innerText is more like Firefox textContent -- visibility
+  // is not concerned. Safari 3 and Chrome innerText is just the visible text.
+  var buf = [];
+  domutil.getVisibleText_(node, buf, true);
+  textContent = buf.join('');
+
+  textContent = textContent.replace(/\xAD/g, '');
+
+  textContent = textContent.replace(/ +/g, ' ');
+  if (textContent != ' ') {
+    textContent = textContent.replace(/^\s*/, '');
+  }
+
+  return textContent;
+};
+
+
+/**
+ * Returns the domutil.getVisibleText without trailing space, if any.
+ *
+ * @param {Node} node The node from which we are getting content.
+ * @return {string} The text content.
+ */
+domutil.getVisibleTextTrim = function(node) {
+  return domutil.getVisibleText(node).replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
+};
+
+
+/**
+ * Recursive support function for text content retrieval.
+ *
+ * @param {Node} node The node from which we are getting content.
+ * @param {Array} buf string buffer.
+ * @param {boolean} normalizeWhitespace Whether to normalize whitespace.
+ * @private
+ */
+domutil.getVisibleText_ = function(node, buf, normalizeWhitespace) {
+  var TAGS_TO_IGNORE_ = {
+    'SCRIPT': 1,
+    'STYLE': 1,
+    'HEAD': 1,
+    'IFRAME': 1,
+    'OBJECT': 1
+  };
+  var PREDEFINED_TAG_VALUES_ = {'IMG': ' ', 'BR': '\n'};
+
+  if (node.nodeName in TAGS_TO_IGNORE_) {
+    // ignore certain tags
+  } else if (node.nodeType == 3) {
+    if (normalizeWhitespace) {
+      buf.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));
+    } else {
+      buf.push(node.nodeValue);
+    }
+  } else if (!domutil.isVisible(node)) {
+    // ignore invisible node
+    // this has to be after the check for NodeType.TEXT because text node
+    // does not have style.
+  } else if (node.nodeName in PREDEFINED_TAG_VALUES_) {
+    buf.push(PREDEFINED_TAG_VALUES_[node.nodeName]);
+  } else {
+    var child = node.firstChild;
+    while (child) {
+      domutil.getVisibleText_(child, buf, normalizeWhitespace);
+      child = child.nextSibling;
+    }
+  }
+};
+

Added: 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/index.html
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/index.html?rev=756236&view=auto
==============================================================================
--- 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/index.html
 (added)
+++ 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/index.html
 Thu Mar 19 22:17:49 2009
@@ -0,0 +1,179 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+  <head>
+    <title>OpenSocial templates JsUnit tests</title>
+    <script src="../../lib/JsUtil.js"></script>
+    <script src="../../lib/JsUnit.js"></script>
+    <script>
+      // TODO: These adapters are needed because the tests were originally
+      // created with a different version of JSUnit in mind. Refactor this file
+      // and the various test files to use the TestCase class infrastructure.
+      var _assert = new Assert();
+      function assertTrue(a) {
+        _assert.assertTrue(a);
+      }
+      function assertEquals(a,b) {
+        _assert.assertEquals(a,b);
+      }
+      function assertNotNull(a) {
+        _assert.assertNotNull(a);
+      }
+      function assertContains(a,b) {
+        _assert.assertTrue(b.indexOf(a) >= 0);
+      }
+    </script>
+    
+    <!-- the set of js files that make up opensocial-templates feature, as 
they appear in feature.xml -->
+    <script 
src="http://google-jstemplate.googlecode.com/svn/trunk/util.js";></script>
+    <script 
src="http://google-jstemplate.googlecode.com/svn/trunk/jsevalcontext.js";></script>
+    <script 
src="http://google-jstemplate.googlecode.com/svn/trunk/jstemplate.js";></script>
+    <script 
src="../../../../main/javascript/features/opensocial-template/jstemplate_debug.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-data-context/datacontext.js"></script>
+    <script 
src="../../../../main/javascript/features/xmlutil/xmlutil.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-data/data.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-templates/base.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-templates/namespaces.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-templates/util.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-templates/template.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-templates/compiler.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-templates/loader.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-templates/container.js"></script>
+    <script 
src="../../../../main/javascript/features/opensocial-templates/os.js"></script>
+    <!-- the JsUnit tests -->
+    <script src="domutil.js"></script>
+    <script type="text/javascript" src="compiler_test.js"></script>
+    <script type="text/javascript" src="container_test.js"></script>
+    <script type="text/javascript" src="loader_test.js"></script>
+    <script type="text/javascript" src="os_test.js"></script>
+    <script type="text/javascript" src="util_test.js"></script>
+    <script type="text/javascript" src="template_test.js"></script>
+    <!-- JsUnit work-around for non-FireFox browsers -->
+    <script type="text/javascript">
+      function exposeTestFunctionNames() {
+        var testSource = typeof RuntimeObject != 'undefined' ?
+                         RuntimeObject('test' + '*') : self;
+        var testFunctionNames = [];
+        for (var i in testSource) {
+          if (i.substring(0, 4) == 'test' && typeof(testSource[i]) == 
'function')
+           testFunctionNames.push(i);
+        }
+        return testFunctionNames;
+      }
+
+      function runAllTests() {
+        var log = function(msg, forcePage) {
+          if (window.console && !forcePage) {
+            console.log(msg);
+            return;
+          } 
+          var div = document.createElement("div");
+          div.appendChild(document.createTextNode(msg));
+          document.body.appendChild(div); 
+        };
+        var tests = exposeTestFunctionNames();
+        var failed = 0;
+        for (var i = 0; i < tests.length; i++) {
+          log(tests[i]);
+          try {
+            window[tests[i]]();
+            log("OK");
+          } catch (e) {
+            log("FAIL: " + e);
+            failed++;
+          }
+        }
+        log("All finished. " + i + " run. " + failed + " failed.", true);
+      }
+      
+      os.createNamespace("test", "http://www.google.com/#test";);
+    </script>
+  </head>
+  <body>
+    <input type="button" onclick="runAllTests()" value="Run tests"/>
+    <script type="text/os-template" tag="os:Test">tag template</script>
+    <script type="text/os-template">
+      <div id="test"><os:Test/></div>
+    </script>
+
+    <div style="display: none">
+      <div id="domSource">
+        <ul>
+          <li>one</li>
+          <li>two</li>
+        </ul>
+        <b>bold</b>
+      </div>
+      <div id="domTarget">
+      </div>
+    </div>
+
+    <xmp id="_T_Substitution_attribute" style="display: none">
+      <button id="${id}" style="color: ${color}" a1="value 
${A1}">${text}</button>
+    </xmp>
+    <xmp id="my:user" style="display: none">
+      <a href="${url}">${name}</a> (${$my.foo})
+    </xmp>
+    <xmp id="my:record" style="display: none">
+      <b style="color: ${$my.color}">${title}</b>: <my:user context="user" 
foo="${$my.foo}"/>
+    </xmp>
+    <xmp id="_T_Substitution_nested" style="display: none">
+      <div repeat="users">
+        <my:record color="${color}" foo="${user.id}"/>
+      </div>
+    </xmp>
+
+    <xmp id="_T_Conditional_Number" style="display: none">
+      <span if="42==42">TRUE</span>
+      <span if="!(42==42)">FALSE</span>
+    </xmp>
+    <xmp id="_T_Conditional_String" style="display: none">
+      <span if="'101'=='101'">TRUE</span>
+      <span if="'101'!='101'">FALSE</span>
+    </xmp>
+    <xmp id="_T_Conditional_Mixed" style="display: none">
+      <span if="'101' gt 42">TRUE</span>
+      <span if="'101' lt 42">FALSE</span>
+    </xmp>
+
+    <xmp id="_T_Repeat" style="display: none">
+      <div repeat="entries">
+        ${data}
+      </div>
+    </xmp>
+
+    <xmp id="_T_Options" style="display: none">
+      <select id="options">
+        <option repeat="options" value="${value}">${value}</option>
+      </select>
+    </xmp>
+
+    <xmp id="custom:list" style="display: none">
+      <div repeat="$my.item"><os:renderAll content="header"/><os:renderAll 
content="body"/></div>
+    </xmp>
+
+    <xmp id="_T_List" style="display: none">
+      <custom:list>
+        <item>
+          <header>hello</header>
+          <body>world</body>
+        </item>
+      </custom:list>
+    </xmp>
+
+    <xmp id="_T_Tag_blink" style="display: none">
+      <custom:blink>blink text</custom:blink>
+    </xmp>
+  </body>
+</html>

Modified: 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/template_test.js
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/template_test.js?rev=756236&r1=756235&r2=756236&view=diff
==============================================================================
--- 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/template_test.js
 (original)
+++ 
incubator/shindig/trunk/features/src/test/javascript/features/opensocial-templates/template_test.js
 Thu Mar 19 22:17:49 2009
@@ -384,36 +384,6 @@
   assertEquals(contentNode.value, data.data);
 }
 
-function testTag_blink() {
-  var custom = os.createNamespace("custom", "http://google.com/#custom";);
-  /**
-   * A controller that reproduces blink behavior.
-   */
-  custom.blink = function(node, context) { // returns HTML
-    var root = document.createElement("span");
-    root.appendChild(node.firstChild);
-    function blink() {
-      var isVisible = root.style["visibility"] == "visible";
-      root.style["visibility"] = isVisible ? "hidden" : "visible";
-      setTimeout(blink, 500);
-    }
-    root.onAttach = function() {
-      blink();
-    };
-    return root;
-  };
-
-  Clock.reset();
-  var outputNode = compileAndRender_("_T_Tag_blink");
-  // contentNode is wrapped by <div><span>
-  var contentNode = outputNode.firstChild.firstChild;
-  assertEquals(contentNode.style.visibility, "visible");
-  Clock.tick(500);
-  assertEquals(contentNode.style.visibility, "hidden");
-  Clock.tick(500);
-  assertEquals(contentNode.style.visibility, "visible");
-}
-
 function testHelloWorld() {
   assertTemplateOutput(
     '<div>Hello world!</div>',

Modified: 
incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java?rev=756236&r1=756235&r2=756236&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java
 (original)
+++ 
incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java
 Thu Mar 19 22:17:49 2009
@@ -137,11 +137,6 @@
   }
 
   @Test
-  public void testData() throws Exception {
-    executeAllPageTests("opensocial-data/data_test");
-  }
-
-  @Test
   public void testTemplates() throws Exception {
     executeAllPageTests("opensocial-templates/ost_test");
   }


Reply via email to