Merge branch 'master' of github.com:open-cloud/xos
diff --git a/views/ngXosLib/xosHelpers/spec/services/helpers/form.helpers.test.js b/views/ngXosLib/xosHelpers/spec/services/helpers/form.helpers.test.js
index 4c90cd6..404f63a 100644
--- a/views/ngXosLib/xosHelpers/spec/services/helpers/form.helpers.test.js
+++ b/views/ngXosLib/xosHelpers/spec/services/helpers/form.helpers.test.js
@@ -133,8 +133,14 @@
         });
       });
 
-      it('should convert the fields array in an empty form object', () => {
-        expect(service.parseModelField(fields)).toEqual(modelField);
+      describe('the parseModelField mehtod', () => {
+        it('should convert the fields array in an empty form object', () => {
+          expect(service.parseModelField(fields)).toEqual(modelField);
+        });
+
+        xit('should handle nested config', () => {
+          
+        });
       });
 
       describe('when modelField are provided', () => {
diff --git a/views/ngXosLib/xosHelpers/spec/ui/field.test.js b/views/ngXosLib/xosHelpers/spec/ui/field.test.js
index c308417..f933a01 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/field.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/field.test.js
@@ -8,13 +8,15 @@
   'use strict';
 
   let element, scope, isolatedScope, rootScope, compile;
-  const compileElement = () => {
+  const compileElement = (el) => {
+    element = el;
 
     if(!scope){
       scope = rootScope.$new();
     }
-
-    element = angular.element('<xos-field name="name" field="field" ng-model="ngModel"></xos-field>');
+    if(!angular.isDefined(element)){
+      element = angular.element('<xos-field name="name" field="field" ng-model="ngModel"></xos-field>');
+    }
     compile(element)(scope);
     scope.$digest();
     isolatedScope = element.isolateScope().vm;
@@ -67,7 +69,7 @@
             type: 'number',
             validators: {}
           };
-          compileElement();
+          compileElement(angular.element('<xos-field name="name" field="field"></xos-field>'));
         }
         expect(errorFunctionWrapper).toThrow(new Error('[xosField] Please provide an ng-model'));
       }));
@@ -173,6 +175,10 @@
           expect($(element).find('.boolean-field > button').length).toEqual(2);
         });
 
+        it('should format labels', () => {
+          expect($(element).find('input[name="foo"]').parent().find('label').text()).toBe('Foo:');
+        });
+
         describe('and the model is empty', () => {
           beforeEach(() => {
             scope.ngModel = {
@@ -181,7 +187,7 @@
           });
 
           it('should not print the panel', () => {
-            console.log($(element).find('.panel.object-field'));
+            // console.log($(element).find('.panel.object-field'));
             expect($(element).find('.panel.object-field')).not.toExist()
           });
         });
diff --git a/views/ngXosLib/xosHelpers/spec/ui/form.test.js b/views/ngXosLib/xosHelpers/spec/ui/form.test.js
index 5f73f30..cb9f9e8 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/form.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/form.test.js
@@ -7,28 +7,44 @@
 (function () {
   'use strict';
 
+  let element, scope, isolatedScope, rootScope, compile;
+
+  const compileElement = () => {
+
+    if(!scope){
+      scope = rootScope.$new();
+    }
+
+    element = angular.element(`<xos-form config="config" ng-model="model"></xos-form>`);
+    compile(element)(scope);
+    scope.$digest();
+    isolatedScope = element.isolateScope().vm;
+  }
+
   describe('The xos.helper module', function(){
 
     describe('The xos-form component', () => {
 
-      let element, scope, isolatedScope;
 
       beforeEach(module('xos.helpers'));
 
+      beforeEach(inject(($compile, $rootScope) => {
+        rootScope = $rootScope;
+        compile = $compile;
+      }));
+
       it('should throw an error if no config is specified', inject(($compile, $rootScope) => {
         function errorFunctionWrapper(){
-          $compile(angular.element('<xos-form></xos-form>'))($rootScope);
-          $rootScope.$digest();
+          compileElement();
         }
         expect(errorFunctionWrapper).toThrow(new Error('[xosForm] Please provide a configuration via the "config" attribute'));
       }));
 
       it('should throw an error if no actions is specified', inject(($compile, $rootScope) => {
         function errorFunctionWrapper(){
-          let scope = $rootScope.$new();
+          scope = $rootScope.$new();
           scope.config = 'green';
-          $compile(angular.element('<xos-form config="config"></xos-form>'))(scope);
-          $rootScope.$digest();
+          compileElement();
         }
         expect(errorFunctionWrapper).toThrow(new Error('[xosForm] Please provide an action list in the configuration'));
       }));
@@ -37,8 +53,7 @@
         
         let cb = jasmine.createSpy('callback');
 
-        beforeEach(inject(($compile, $rootScope) => {
-
+        beforeEach(inject(($rootScope) => {
 
           scope = $rootScope.$new();
 
@@ -78,10 +93,7 @@
             }
           };
 
-          element = angular.element(`<xos-form config="config" ng-model="model"></xos-form>`);
-          $compile(element)(scope);
-          scope.$digest();
-          isolatedScope = element.isolateScope().vm;
+          compileElement();
         }));
 
         it('should add excluded properties to the list', () => {
@@ -194,6 +206,53 @@
             expect(isolatedScope.testForm.age.$error.min).toBeTruthy();
           });
         });
+
+        describe('when a deep model is passed', () => {
+
+          beforeEach(inject(($rootScope) => {
+
+            scope = $rootScope.$new();
+
+            scope.config = {
+              exclude: ['excludedField'],
+              formName: 'testForm',
+              actions: [
+                {
+                  label: 'Save',
+                  icon: 'ok', // refers to bootstraps glyphicon
+                  cb: cb,
+                  class: 'success'
+                }
+              ],
+              fields: {
+                object_field: {
+                  field_one: {
+                    label: 'Custom Label'
+                  }
+                }
+              }
+            };
+
+            scope.model = {
+              object_field: {
+                field_one: 'bar',
+                number: 1,
+                email: 'teo@onlab.us'
+              }
+            };
+
+            compileElement();
+          }));
+
+          it('should print nested field', () => {
+            expect($(element).find('input').length).toBe(3);
+          });
+
+          xit('should configure nested fields', () => {
+            let custom_label = $(element).find('input[name=field_one]').parent().find('label');
+            expect(custom_label.text()).toBe('Custom Label');
+          });
+        });
       });
     });
   });
diff --git a/views/ngXosLib/xosHelpers/src/styles/main.scss b/views/ngXosLib/xosHelpers/src/styles/main.scss
index 43c54f6..cc02e6f 100644
--- a/views/ngXosLib/xosHelpers/src/styles/main.scss
+++ b/views/ngXosLib/xosHelpers/src/styles/main.scss
@@ -4,6 +4,7 @@
 @import '../ui_components/dumbComponents/table/table.scss';
 @import '../ui_components/dumbComponents/alert/alert.scss';
 @import '../ui_components/dumbComponents/validation/validation.scss';
+@import '../ui_components/dumbComponents/field/field.scss';
 
 @import '../ui_components/smartComponents/smartTable/smartTable.scss';
 
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js
index c2aa62d..20b7707 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js
@@ -26,6 +26,99 @@
     * }
     * ```
     * @param {mixed} ngModel The field value
+    *
+    * @example
+    
+    # Basic Example
+    
+      <example module="sampleField1">
+        <file name="script.js">
+          angular.module('sampleField1', ['xos.uiComponents'])
+          .factory('_', function($window){
+            return $window._;
+          })
+          .controller('SampleCtrl', function(){
+            this.name = 'input-name';
+            this.field = {label: 'My String Value:', type: 'string'};
+            this.model = 'my string';
+          });
+        </file>
+        <file name="index.html">
+          <div ng-controller="SampleCtrl as vm">
+            <xos-field ng-model="vm.model" name="vm.name" field="vm.field"></xos-field>
+          </div>
+        </file>
+      </example>
+      
+      # Possible Values
+
+      <example module="sampleField2">
+        <file name="script.js">
+          angular.module('sampleField2', ['xos.uiComponents'])
+          .factory('_', function($window){
+            return $window._;
+          })
+          .controller('SampleCtrl', function(){
+            this.field1 = {
+              name: 'number-field',
+              field: {label: 'My Number Value:', type: 'number'},
+              model: 2
+            };
+
+            this.field2 = {
+              name: 'date-field',
+              field: {label: 'My Date Value:', type: 'date'},
+              model: new Date()
+            };
+
+            this.field3 = {
+              name: 'boolean-field',
+              field: {label: 'My Boolean Value:', type: 'boolean'},
+              model: true
+            };
+
+            this.field4 = {
+              name: 'email-field',
+              field: {label: 'My Email Value:', type: 'email'},
+              model: 'sample@domain.us'
+            };
+          });
+        </file>
+        <file name="index.html">
+          <div ng-controller="SampleCtrl as vm">
+            <xos-field ng-model="vm.field1.model" name="vm.field1.name" field="vm.field1.field"></xos-field>
+            <xos-field ng-model="vm.field2.model" name="vm.field2.name" field="vm.field2.field"></xos-field>
+            <xos-field ng-model="vm.field3.model" name="vm.field3.name" field="vm.field3.field"></xos-field>
+            <xos-field ng-model="vm.field4.model" name="vm.field4.name" field="vm.field4.field"></xos-field>
+          </div>
+        </file>
+      </example>
+
+      # This element is recursive
+
+      <example module="sampleField3">
+        <file name="script.js">
+          angular.module('sampleField3', ['xos.uiComponents'])
+          .factory('_', function($window){
+            return $window._;
+          })
+          .controller('SampleCtrl', function(){
+            this.name = 'input-name';
+            this.field = {label: 'My Object Value:', type: 'object'};
+            this.model = {
+              name: 'Jhon',
+              age: '25',
+              email: 'jhon@thewall.ru',
+              active: true
+            };
+          });
+        </file>
+        <file name="index.html">
+          <div ng-controller="SampleCtrl as vm">
+            <xos-field ng-model="vm.model" name="vm.name" field="vm.field"></xos-field>
+          </div>
+        </file>
+      </example>
     */
   .directive('xosField', function(RecursionHelper){
     return {
@@ -69,7 +162,7 @@
                 <div ng-repeat="(k, v) in vm.ngModel">
                   <xos-field
                     name="k"
-                    field="{label: k, type: vm.getType(v)}"
+                    field="{label: vm.formatLabel(k), type: vm.getType(v)}"
                     ng-model="v">
                   </xos-field>
                 </div>
@@ -82,7 +175,7 @@
       compile: function (element) {
         return RecursionHelper.compile(element);
       },
-      controller: function(XosFormHelpers){
+      controller: function($attrs, XosFormHelpers, LabelFormatter){
         // console.log('Field: ', this.name, this.field, this.ngModel);
         if(!this.name){
           throw new Error('[xosField] Please provide a field name');
@@ -90,11 +183,12 @@
         if(!this.field){
           throw new Error('[xosField] Please provide a field definition');
         }
-        if(!angular.isDefined(this.ngModel)){
+        if(!$attrs.ngModel){
           throw new Error('[xosField] Please provide an ng-model');
         }
 
         this.getType = XosFormHelpers._getFieldFormat;
+        this.formatLabel = LabelFormatter.format;
 
         this.isEmptyObject = o => Object.keys(o).length === 0;
       }
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.scss b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.scss
new file mode 100644
index 0000000..8dd7ca4
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.scss
@@ -0,0 +1,3 @@
+xos-field {
+  display: block;
+}
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/ui-components.module.js b/views/ngXosLib/xosHelpers/src/ui_components/ui-components.module.js
index ad3dc49..d94e59d 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/ui-components.module.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/ui-components.module.js
@@ -24,6 +24,7 @@
   **/
 
   angular.module('xos.uiComponents', [
-    'chart.js'
+    'chart.js',
+    'RecursionHelper'
   ])
 })();
diff --git a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
index 4ffc99d..fe246a6 100644
--- a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
+++ b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
@@ -24,8 +24,7 @@
         'ngResource',
         'ngAnimate',
         'bugSnag',
-        'xos.uiComponents',
-        'RecursionHelper'
+        'xos.uiComponents'
       ])
       .config(config)
 
diff --git a/views/ngXosViews/subscribers/karma.conf.js b/views/ngXosViews/subscribers/karma.conf.js
index 4123be9..0e9c4d8 100644
--- a/views/ngXosViews/subscribers/karma.conf.js
+++ b/views/ngXosViews/subscribers/karma.conf.js
@@ -26,6 +26,7 @@
 
     // list of files / patterns to load in the browser
     files: bowerComponents.concat([
+      'node_modules/babel-polyfill/dist/polyfill.js',
       '../../../xos/core/xoslib/static/js/vendor/ngXosVendor.js',
       '../../../xos/core/xoslib/static/js/vendor/ngXosHelpers.js',
       'src/js/**/*.js',
@@ -78,7 +79,10 @@
 
     // start these browsers
     // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
-    browsers: ['PhantomJS'],
+    browsers: [
+      'PhantomJS',
+      'Chrome'
+    ],
 
 
     // Continuous Integration mode
diff --git a/views/ngXosViews/subscribers/package.json b/views/ngXosViews/subscribers/package.json
index 3d5def5..96a2326 100644
--- a/views/ngXosViews/subscribers/package.json
+++ b/views/ngXosViews/subscribers/package.json
@@ -21,6 +21,7 @@
   "dependencies": {},
   "devDependencies": {
     "autoprefixer": "^6.3.3",
+    "babel-polyfill": "^6.9.0",
     "browser-sync": "^2.9.11",
     "css-mqpacker": "^4.0.0",
     "csswring": "^4.2.1",
@@ -48,6 +49,7 @@
     "jasmine-core": "~2.3.4",
     "karma": "^0.13.14",
     "karma-babel-preprocessor": "~5.2.2",
+    "karma-chrome-launcher": "^1.0.1",
     "karma-coverage": "^0.5.3",
     "karma-jasmine": "~0.3.6",
     "karma-mocha-reporter": "~1.1.1",
diff --git a/views/ngXosViews/subscribers/spec/sample.test.js b/views/ngXosViews/subscribers/spec/sample.test.js
index 9b06600..1b7796c 100644
--- a/views/ngXosViews/subscribers/spec/sample.test.js
+++ b/views/ngXosViews/subscribers/spec/sample.test.js
@@ -1,7 +1,17 @@
 'use strict';
 
-describe('The User List', () => {
+describe('The Subscriber View', () => {
   
+  const subscribersList = [
+    {
+      humanReadableName: 'cordSubscriber-1',
+      features: {cdn: false, uplink_speed: 1000000000, downlink_speed: 1000000000, uverse: true, status: 'enabled'},
+      id: 1,
+      identity: {account_num: '123', name: 'Stanford'},
+      related: {}
+    }
+  ];
+
   var scope, element, isolatedScope, httpBackend;
 
   beforeEach(module('xos.subscribers'));
@@ -10,28 +20,34 @@
   beforeEach(inject(function($httpBackend, $compile, $rootScope){
     
     httpBackend = $httpBackend;
-    // Setting up mock request
-    $httpBackend.expectGET('/api/core/users/?no_hyperlinks=1').respond([
-      {
-        email: 'matteo.scandolo@gmail.com',
-        firstname: 'Matteo',
-        lastname: 'Scandolo' 
-      }
-    ]);
+    
+    httpBackend.whenGET('/api/tenant/cord/subscriber/?no_hyperlinks=1').respond(subscribersList);
   
     scope = $rootScope.$new();
-    element = angular.element('<users-list></users-list>');
+    element = angular.element('<subscribers-list></subscribers-list>');
     $compile(element)(scope);
     scope.$digest();
     isolatedScope = element.isolateScope().vm;
   }));
 
-  xit('should load 1 users', () => {
+  it('should load 1 subscriber', () => {
+    // this
     httpBackend.flush();
-    expect(isolatedScope.users.length).toBe(1);
-    expect(isolatedScope.users[0].email).toEqual('matteo.scandolo@gmail.com');
-    expect(isolatedScope.users[0].firstname).toEqual('Matteo');
-    expect(isolatedScope.users[0].lastname).toEqual('Scandolo');
+    scope.$digest();
+    let table = $(element).find('table');
+    let tr = table.find('tbody:last-child tr');
+    // let tds = $(tr[1]).find('td');
+    // console.log(tr);
+    expect(tr.length).toBe(1);
+    // expect($(tds[0]).html()).toBe('cordSubscriber-1')
+  });
+
+  it('should configure xos-smart-table', () => {
+    expect(isolatedScope.smartTableConfig).toEqual({resource: 'Subscribers'});
+  });
+
+  it('should render xos-smart-table', () => {
+    expect($(element).find('xos-smart-table').length).toBe(1);
   });
 
 });
\ No newline at end of file
diff --git a/views/ngXosViews/subscribers/src/js/main.js b/views/ngXosViews/subscribers/src/js/main.js
index 838efa0..203e9ff 100644
--- a/views/ngXosViews/subscribers/src/js/main.js
+++ b/views/ngXosViews/subscribers/src/js/main.js
@@ -28,27 +28,6 @@
       this.smartTableConfig = {
         resource: 'Subscribers'
       };
-
-      this.model = {
-        label: {
-          name: 'aaa'
-        },
-        empty: {}
-      }
-      this.config = {
-        exclude: ['password', 'last_login'],
-        formName: 'sampleForm',
-        actions: [
-          {
-            label: 'Save',
-            icon: 'ok', // refers to bootstraps glyphicon
-            cb: (user) => { // receive the model
-              console.log(user);
-            },
-            class: 'success'
-          }
-        ]
-      };
     }
   };
 });
\ No newline at end of file
diff --git a/views/ngXosViews/subscribers/src/templates/subscribers-list.tpl.html b/views/ngXosViews/subscribers/src/templates/subscribers-list.tpl.html
index 2e9298b..d6e755b 100644
--- a/views/ngXosViews/subscribers/src/templates/subscribers-list.tpl.html
+++ b/views/ngXosViews/subscribers/src/templates/subscribers-list.tpl.html
@@ -1,2 +1 @@
-<!-- <xos-form ng-model="vm.model" config="vm.config"></xos-form> -->
 <xos-smart-table config="vm.smartTableConfig"></xos-smart-table>
\ No newline at end of file