[CORD-1338] Inline navigation for related models

Change-Id: I58ff4a4675d1ce1140fe162f1f8360f2dc9a6527
diff --git a/src/app/views/crud/crud.relations.service.spec.ts b/src/app/views/crud/crud.relations.service.spec.ts
new file mode 100644
index 0000000..8c0f2f9
--- /dev/null
+++ b/src/app/views/crud/crud.relations.service.spec.ts
@@ -0,0 +1,196 @@
+import {
+  IXosCrudRelationService, XosCrudRelationService, IXosCrudRelationFormTabData,
+  IXosCrudRelationTableTabData
+} from './crud.relations.service';
+import {BehaviorSubject} from 'rxjs';
+import {ConfigHelpers} from '../../core/services/helpers/config.helpers';
+
+const XosModelStoreMock = {
+  get: null,
+  query: null
+};
+
+const XosModelDiscovererMock = {
+  get: null
+};
+
+let service, scope;
+
+describe('The XosCrudRelation service', () => {
+  beforeEach(() => {
+    angular
+      .module('test', ['ui.router', 'toastr'])
+      .service('XosCrudRelation', XosCrudRelationService)
+      .value('XosModelStore', XosModelStoreMock)
+      .value('XosModelDiscoverer', XosModelDiscovererMock)
+      .service('ConfigHelpers', ConfigHelpers);
+
+    angular.mock.module('test');
+  });
+
+  beforeEach(angular.mock.inject((XosCrudRelation: IXosCrudRelationService, $rootScope: ng.IScope) => {
+    service = XosCrudRelation;
+    scope = $rootScope;
+  }));
+
+  it('should have the correct methods', () => {
+    expect(service.getModel).toBeDefined();
+    expect(service.getModels).toBeDefined();
+    expect(service.existsRelatedItem).toBeDefined();
+  });
+
+  describe('the existsRelatedItem method', () => {
+    it('should return true if the we have a reference to the related model', () => {
+      const relation = {
+        model: 'Test',
+        type: 'manytoone',
+        on_field: 'test'
+      };
+      const item = {test_id: 5};
+
+      const res = service.existsRelatedItem(relation, item);
+      expect(res).toBeTruthy();
+    });
+    it('should return false if the we don\'t have a reference to the related model', () => {
+      const relation = {
+        model: 'Test',
+        type: 'manytoone',
+        on_field: 'test'
+      };
+      const item = {foo: 5};
+
+      const res = service.existsRelatedItem(relation, item);
+      expect(res).toBeFalsy();
+    });
+  });
+
+  describe('the getHumanReadableOnField method', () => {
+    it('should return a human readable version of the on_field param', () => {
+      const relation = {
+        model: 'Test',
+        type: 'onetomany',
+        on_field: 'relate_to_test'
+      };
+
+      const res = service.getHumanReadableOnField(relation, 'Instance');
+      expect(res).toEqual('[Relate to test]');
+    });
+
+    it('should return am empty string if the on_field param equal the model param', () => {
+      const relation = {
+        model: 'Test',
+        type: 'onetomany',
+        on_field: 'test'
+      };
+
+      const res = service.getHumanReadableOnField(relation, 'Instance');
+      expect(res).toEqual('');
+    });
+
+    it('should return am empty string if the type on_field equal the base model', () => {
+      const relation = {
+        model: 'Test',
+        type: 'manytoone',
+        on_field: 'instance'
+      };
+
+      const res = service.getHumanReadableOnField(relation, 'Instance');
+      expect(res).toEqual('');
+    });
+  });
+
+  describe('the getModel method', () => {
+    it('should return the tab config for a single object', (done) => {
+      const relation = {
+        model: 'Test',
+        type: 'manytoone',
+        on_field: 'test'
+      };
+
+      const resModel = {foo: 'bar'};
+      const resFormCfg = {form: 'config'};
+
+      spyOn(XosModelStoreMock, 'get').and.callFake(() => {
+        const subject = new BehaviorSubject({});
+        subject.next(resModel);
+        return subject.asObservable();
+      });
+      spyOn(XosModelDiscovererMock, 'get').and.returnValue({formCfg: resFormCfg});
+
+      service.getModel(relation, '5')
+        .then((res: IXosCrudRelationFormTabData) => {
+          expect(res.model).toEqual(resModel);
+          expect(res.class).toEqual('full');
+          expect(res.formConfig).toEqual(resFormCfg);
+          done();
+        });
+      scope.$apply();
+    });
+  });
+
+  describe('the getModels method', () => {
+    it('should return one related model', (done) => {
+      const relation = {
+        model: 'Test',
+        type: 'onetomany',
+        on_field: 'test'
+      };
+
+      const resModels = [
+        {test_id: 5},
+        {test_id: 25}
+      ];
+      const resTableCfg = {table: 'config'};
+
+      spyOn(XosModelStoreMock, 'query').and.callFake(() => {
+        const subject = new BehaviorSubject(resModels);
+        return subject.asObservable();
+      });
+      spyOn(XosModelDiscovererMock, 'get').and.returnValue({tableCfg: resTableCfg});
+
+      service.getModels(relation, 5)
+        .then((res: IXosCrudRelationTableTabData) => {
+          expect(res.model.length).toEqual(1);
+          expect(res.class).toEqual('full');
+          expect(res.tableConfig).toEqual({
+            table: 'config',
+            filter: null
+          });
+          done();
+        });
+      scope.$apply();
+    });
+
+    it('should not return related models', (done) => {
+      const relation = {
+        model: 'Test',
+        type: 'onetomany',
+        on_field: 'test'
+      };
+
+      const resModels = [
+        {test_id: 15},
+        {test_id: 25}
+      ];
+      const resTableCfg = {table: 'config'};
+
+      spyOn(XosModelStoreMock, 'query').and.callFake(() => {
+        const subject = new BehaviorSubject(resModels);
+        return subject.asObservable();
+      });
+      spyOn(XosModelDiscovererMock, 'get').and.returnValue({tableCfg: resTableCfg});
+
+      service.getModels(relation, 5)
+        .then((res: IXosCrudRelationTableTabData) => {
+          expect(res.model.length).toEqual(0);
+          expect(res.class).toEqual('empty');
+          expect(res.tableConfig).toEqual({
+            table: 'config',
+            filter: null
+          });
+          done();
+        });
+      scope.$apply();
+    });
+  });
+});