Browse Source

commands per containerId, simple push, refactoring

Daniel Zlotin 8 years ago
parent
commit
f9adf71574

+ 9
- 0
ios/RNNBridgeModule.m View File

@@ -19,6 +19,15 @@ RCT_EXPORT_METHOD(setRoot:(NSDictionary*)layout)
19 19
 	[UIApplication.sharedApplication.delegate.window makeKeyAndVisible];
20 20
 }
21 21
 
22
+RCT_EXPORT_METHOD(push:(NSString*)containerId layout:(NSDictionary*)layout)
23
+{
24
+	[self assertReady];
25
+	//TODO make this not shitty
26
+	UIViewController* newVc = [[RNNControllerFactory new] createRootViewController:layout];
27
+	id vc = [UIApplication.sharedApplication.delegate.window.rootViewController childViewControllers][0];
28
+	[[vc navigationController]pushViewController:newVc animated:true];
29
+}
30
+
22 31
 -(void)assertReady
23 32
 {
24 33
 	if (!RNN.instance.isReadyToReceiveCommands) {

+ 1
- 1
playground/e2e/app.test.js View File

@@ -13,7 +13,7 @@ describe('app', () => {
13 13
     expect(elementByLabel('Hello from a function!')).toBeVisible();
14 14
   });
15 15
 
16
-  xit('push screen', () => {
16
+  it('push screen', () => {
17 17
     elementByLabel('Push').tap();
18 18
     expect(elementByLabel('Pushed screen')).toBeVisible();
19 19
   });

+ 13
- 3
src/Navigation.js View File

@@ -4,7 +4,10 @@ import UniqueIdProvider from './adapters/UniqueIdProvider';
4 4
 
5 5
 import Store from './containers/Store';
6 6
 import ContainerRegistry from './containers/ContainerRegistry';
7
-import Commands from './commands/Commands';
7
+import AppCommands from './commands/AppCommands';
8
+import ContainerCommands from './commands/ContainerCommands';
9
+import LayoutTreeParser from './commands/LayoutTreeParser';
10
+import LayoutTreeCrawler from './commands/LayoutTreeCrawler';
8 11
 
9 12
 class Navigation {
10 13
   constructor() {
@@ -12,7 +15,10 @@ class Navigation {
12 15
     this.nativeEventsReceiver = new NativeEventsReceiver();
13 16
     this.uniqueIdProvider = new UniqueIdProvider();
14 17
     this.containerRegistry = new ContainerRegistry(this.store);
15
-    this.commands = new Commands(new NativeCommandsSender(), this.uniqueIdProvider, this.store);
18
+    this.layoutTreeParser = new LayoutTreeParser();
19
+    this.layoutTreeCrawler = new LayoutTreeCrawler(this.uniqueIdProvider, this.store);
20
+    this.nativeCommandsSender = new NativeCommandsSender();
21
+    this.appCommands = new AppCommands(this.nativeCommandsSender, this.layoutTreeParser, this.layoutTreeCrawler);
16 22
   }
17 23
 
18 24
   registerContainer(containerName, getContainerFunc) {
@@ -20,12 +26,16 @@ class Navigation {
20 26
   }
21 27
 
22 28
   setRoot(params) {
23
-    return this.commands.setRoot(params);
29
+    return this.appCommands.setRoot(params);
24 30
   }
25 31
 
26 32
   events() {
27 33
     return this.nativeEventsReceiver;
28 34
   }
35
+
36
+  on(containerId) {
37
+    return new ContainerCommands(containerId, this.nativeCommandsSender, this.layoutTreeParser, this.layoutTreeCrawler);
38
+  }
29 39
 }
30 40
 
31 41
 const singleton = new Navigation();

+ 10
- 4
src/Navigation.test.js View File

@@ -2,12 +2,14 @@ describe('Navigation', () => {
2 2
   let Navigation;
3 3
 
4 4
   beforeEach(() => {
5
+    //TODO don't mock here, just create the actual object and assert on basic behavior
5 6
     jest.mock('./adapters/UniqueIdProvider');
6 7
     jest.mock('./adapters/NativeCommandsSender');
7 8
     jest.mock('./adapters/NativeEventsReceiver');
8 9
 
9 10
     jest.mock('./containers/ContainerRegistry');
10
-    jest.mock('./commands/Commands');
11
+    jest.mock('./commands/AppCommands');
12
+    jest.mock('./commands/ContainerCommands');
11 13
 
12 14
     Navigation = require('./Navigation');
13 15
   });
@@ -21,16 +23,20 @@ describe('Navigation', () => {
21 23
   });
22 24
 
23 25
   it('setRoot delegates to Commands', async () => {
24
-    Navigation.commands.setRoot.mockReturnValue(Promise.resolve('result'));
26
+    Navigation.appCommands.setRoot.mockReturnValue(Promise.resolve('result'));
25 27
     const params = {};
26 28
     const result = await Navigation.setRoot(params);
27 29
     expect(result).toEqual('result');
28
-    expect(Navigation.commands.setRoot).toHaveBeenCalledTimes(1);
29
-    expect(Navigation.commands.setRoot).toHaveBeenCalledWith(params);
30
+    expect(Navigation.appCommands.setRoot).toHaveBeenCalledTimes(1);
31
+    expect(Navigation.appCommands.setRoot).toHaveBeenCalledWith(params);
30 32
   });
31 33
 
32 34
   it('events return the events registry', () => {
33 35
     expect(Navigation.events()).toBeDefined();
34 36
     expect(Navigation.events().onAppLaunched).toBeInstanceOf(Function);
35 37
   });
38
+
39
+  it('on containerId returns an object that performs commands on a container', () => {
40
+    expect(Navigation.on('myUniqueId').push).toBeInstanceOf(Function);
41
+  });
36 42
 });

+ 6
- 0
src/adapters/NativeCommandsSender.js View File

@@ -9,4 +9,10 @@ export default class NativeCommandsSender {
9 9
     this.nativeCommandsModule.setRoot(layoutTree);
10 10
     return Promise.resolve(layoutTree);
11 11
   }
12
+
13
+  push(onContainerId, layout) {
14
+    this.nativeCommandsModule.push(onContainerId, layout);
15
+    return Promise.resolve(layout);
16
+  }
12 17
 }
18
+

+ 1
- 0
src/adapters/NativeCommandsSender.mock.js View File

@@ -0,0 +1 @@
1
+export default jest.genMockFromModule('./NativeCommandsSender').default;

+ 13
- 3
src/adapters/NativeCommandsSender.test.js View File

@@ -1,12 +1,15 @@
1
+import { NativeModules } from 'react-native';
2
+import NativeCommandsSender from './NativeCommandsSender';
3
+
1 4
 describe('NativeCommandsSender', () => {
2 5
   let uut, mockNativeModule;
3 6
 
4 7
   beforeEach(() => {
5 8
     mockNativeModule = {
6
-      setRoot: jest.fn()
9
+      setRoot: jest.fn(),
10
+      push: jest.fn()
7 11
     };
8
-    require('react-native').NativeModules.RNNBridgeModule = mockNativeModule;
9
-    const NativeCommandsSender = require('./NativeCommandsSender').default;
12
+    NativeModules.RNNBridgeModule = mockNativeModule;
10 13
     uut = new NativeCommandsSender();
11 14
   });
12 15
 
@@ -19,4 +22,11 @@ describe('NativeCommandsSender', () => {
19 22
     const result = await uut.setRoot({});
20 23
     expect(result).toBeDefined();
21 24
   });
25
+
26
+  it('push sends to native and resolves with promise', async () => {
27
+    const theNewContainer = {};
28
+    const result = await uut.push('theContainerId', theNewContainer);
29
+    expect(mockNativeModule.push).toHaveBeenCalledTimes(1);
30
+    expect(result).toBeDefined();
31
+  });
22 32
 });

+ 1
- 0
src/adapters/NativeEventsReceiver.mock.js View File

@@ -0,0 +1 @@
1
+export default jest.genMockFromModule('./NativeEventsReceiver').default;

+ 4
- 2
src/adapters/NativeEventsReceiver.test.js View File

@@ -1,10 +1,12 @@
1
+import { NativeModules } from 'react-native';
2
+import NativeEventsReceiver from './NativeEventsReceiver';
3
+
1 4
 describe('NativeEventsReceiver', () => {
2 5
   let uut;
3 6
   const eventEmitterMock = { addListener: jest.fn() };
4 7
 
5 8
   beforeEach(() => {
6
-    require('react-native').NativeModules.RNNEventEmitter = {};
7
-    const NativeEventsReceiver = require('./NativeEventsReceiver').default;
9
+    NativeModules.RNNEventEmitter = {};
8 10
     uut = new NativeEventsReceiver();
9 11
     uut.emitter = eventEmitterMock;
10 12
   });

+ 6
- 0
src/adapters/UniqueIdProvider.mock.js View File

@@ -0,0 +1,6 @@
1
+export default class UniqueIdProvider {
2
+  generate(prefix) {
3
+    return `${prefix}+UNIQUE_ID`;
4
+  }
5
+}
6
+

+ 8
- 6
src/adapters/UniqueIdProvider.test.js View File

@@ -1,19 +1,21 @@
1
+import UniqueIdProvider from './UniqueIdProvider';
2
+
1 3
 describe('UniqueIdProvider', () => {
2 4
   let uut;
3 5
 
4 6
   beforeEach(() => {
5
-    const UniqueIdProvider = require('./UniqueIdProvider').default;
6 7
     uut = new UniqueIdProvider();
7 8
   });
8 9
 
9
-  it('provides uniqueId', () => {
10
+  it('provides uniqueId with optional prefix', () => {
10 11
     expect(uut.generate()).toEqual('1');
11 12
     expect(uut.generate()).toEqual('2');
13
+    expect(uut.generate('prefix')).toEqual('prefix3');
14
+    expect(uut.generate('prefix')).toEqual('prefix4');
15
+    expect(uut.generate('other')).toEqual('other5');
12 16
   });
13 17
 
14
-  it('provides with prefix', () => {
15
-    expect(uut.generate('prefix')).toEqual('prefix1');
16
-    expect(uut.generate('prefix')).toEqual('prefix2');
17
-    expect(uut.generate('other')).toEqual('other3');
18
+  it('id is unique across instances', () => {
19
+    expect(uut.generate()).not.toEqual('1');
18 20
   });
19 21
 });

+ 17
- 0
src/commands/AppCommands.js View File

@@ -0,0 +1,17 @@
1
+import _ from 'lodash';
2
+
3
+export default class AppCommands {
4
+  constructor(nativeCommandsSender, layoutTreeParser, layoutTreeCrawler) {
5
+    this.nativeCommandsSender = nativeCommandsSender;
6
+    this.layoutTreeParser = layoutTreeParser;
7
+    this.layoutTreeCrawler = layoutTreeCrawler;
8
+  }
9
+
10
+  setRoot(simpleApi) {
11
+    const input = _.cloneDeep(simpleApi);
12
+    const layout = this.layoutTreeParser.parseFromSimpleJSON(input);
13
+    this.layoutTreeCrawler.crawl(layout);
14
+    return this.nativeCommandsSender.setRoot(layout);
15
+  }
16
+}
17
+

src/commands/Commands.test.js → src/commands/AppCommands.test.js View File

@@ -1,21 +1,22 @@
1 1
 import * as SimpleLayouts from './SimpleLayouts';
2
+import LayoutTreeParser from './LayoutTreeParser';
3
+import LayoutTreeCrawler from './LayoutTreeCrawler';
4
+import Store from '../containers/Store';
5
+import UniqueIdProvider from '../adapters/UniqueIdProvider.mock';
6
+import NativeCommandsSender from '../adapters/NativeCommandsSender.mock';
2 7
 
3
-describe('Commands', () => {
8
+import AppCommands from './AppCommands';
9
+
10
+describe('AppCommands', () => {
4 11
   let uut;
5 12
   let mockCommandsSender;
6 13
   let store;
7 14
 
8 15
   beforeEach(() => {
9
-    const NativeCommandsSender = jest.genMockFromModule('../adapters/NativeCommandsSender').default;
10 16
     mockCommandsSender = new NativeCommandsSender();
11
-
12
-    const mockIdProvider = { generate: (prefix) => `${prefix}+UNIQUE_ID` };
13
-
14
-    const Store = require('../containers/Store').default;
15 17
     store = new Store();
16 18
 
17
-    const Commands = require('./Commands').default;
18
-    uut = new Commands(mockCommandsSender, mockIdProvider, store);
19
+    uut = new AppCommands(mockCommandsSender, new LayoutTreeParser(), new LayoutTreeCrawler(new UniqueIdProvider(), store));
19 20
   });
20 21
 
21 22
   describe('setRoot', () => {

+ 0
- 19
src/commands/Commands.js View File

@@ -1,19 +0,0 @@
1
-import _ from 'lodash';
2
-import LayoutTreeParser from './LayoutTreeParser';
3
-import LayoutTreeCrawler from './LayoutTreeCrawler';
4
-
5
-export default class Commands {
6
-  constructor(nativeCommandsSender, uniqueIdProvider, store) {
7
-    this.nativeCommandsSender = nativeCommandsSender;
8
-    this.uniqueIdProvider = uniqueIdProvider;
9
-    this.layoutTreeParser = new LayoutTreeParser();
10
-    this.layoutTreeCrawler = new LayoutTreeCrawler(uniqueIdProvider, store);
11
-  }
12
-
13
-  setRoot(simpleApi) {
14
-    const input = _.cloneDeep(simpleApi);
15
-    const layout = this.layoutTreeParser.parseFromSimpleJSON(input);
16
-    this.layoutTreeCrawler.crawl(layout);
17
-    return this.nativeCommandsSender.setRoot(layout);
18
-  }
19
-}

+ 17
- 0
src/commands/ContainerCommands.js View File

@@ -0,0 +1,17 @@
1
+import _ from 'lodash';
2
+
3
+export default class ContainerCommands {
4
+  constructor(containerId, nativeCommandsSender, layoutTreeParser, layoutTreeCrawler) {
5
+    this.containerId = containerId;
6
+    this.nativeCommandsSender = nativeCommandsSender;
7
+    this.layoutTreeParser = layoutTreeParser;
8
+    this.layoutTreeCrawler = layoutTreeCrawler;
9
+  }
10
+
11
+  push(containerData) {
12
+    const input = _.cloneDeep(containerData);
13
+    const layout = this.layoutTreeParser.createContainer(input);
14
+    this.layoutTreeCrawler.crawl(layout);
15
+    return this.nativeCommandsSender.push(this.containerId, layout);
16
+  }
17
+}

+ 47
- 0
src/commands/ContainerCommands.test.js View File

@@ -0,0 +1,47 @@
1
+import NativeCommandsSender from '../adapters/NativeCommandsSender.mock';
2
+import UniqueIdProvider from '../adapters/UniqueIdProvider.mock';
3
+import Store from '../containers/Store';
4
+import LayoutTreeParser from './LayoutTreeParser';
5
+import LayoutTreeCrawler from './LayoutTreeCrawler';
6
+
7
+import ContainerCommands from './ContainerCommands';
8
+
9
+describe('ContainerCommands', () => {
10
+  let uut;
11
+  let mockCommandsSender;
12
+  const containerId = 'myUniqueId';
13
+
14
+  beforeEach(() => {
15
+    mockCommandsSender = new NativeCommandsSender();
16
+    uut = new ContainerCommands(containerId, mockCommandsSender, new LayoutTreeParser(), new LayoutTreeCrawler(new UniqueIdProvider(), new Store()));
17
+  });
18
+
19
+  it('holds containerId', () => {
20
+    expect(uut.containerId).toEqual('myUniqueId');
21
+  });
22
+
23
+  describe('push', () => {
24
+    it('deep clones input to avoid mutation errors', () => {
25
+      const obj = {};
26
+      uut.push({ inner: { foo: obj } });
27
+      expect(mockCommandsSender.push.mock.calls[0][1].data.inner.foo).not.toBe(obj);
28
+    });
29
+
30
+    it('resolves with the parsed layout', async () => {
31
+      mockCommandsSender.push.mockReturnValue(Promise.resolve('the resolved layout'));
32
+      const result = await uut.push({ name: 'com.example.MyScreen' });
33
+      expect(result).toEqual('the resolved layout');
34
+    });
35
+
36
+    it('parses into correct layout node and sends to native', () => {
37
+      uut.push({ name: 'com.example.MyScreen' });
38
+      expect(mockCommandsSender.push).toHaveBeenCalledTimes(1);
39
+      expect(mockCommandsSender.push).toHaveBeenCalledWith('myUniqueId', {
40
+        type: 'Container',
41
+        id: 'Container+UNIQUE_ID',
42
+        data: { name: 'com.example.MyScreen' },
43
+        children: []
44
+      });
45
+    });
46
+  });
47
+});

+ 4
- 5
src/commands/LayoutTreeCrawler.test.js View File

@@ -1,16 +1,15 @@
1 1
 import LayoutTypes from './LayoutTypes';
2
+import LayoutTreeCrawler from './LayoutTreeCrawler';
3
+import Store from '../containers/Store';
4
+import UniqueIdProvider from '../adapters/UniqueIdProvider.mock';
2 5
 
3 6
 describe('LayoutTreeCrawler', () => {
4 7
   let uut;
5 8
   let store;
6 9
 
7 10
   beforeEach(() => {
8
-    const LayoutTreeCrawler = require('./LayoutTreeCrawler').default;
9
-    const uniqueIdProvider = { generate: (prefix) => `${prefix}+UNIQUE_ID` };
10
-    const Store = require('../containers/Store').default;
11 11
     store = new Store();
12
-
13
-    uut = new LayoutTreeCrawler(uniqueIdProvider, store);
12
+    uut = new LayoutTreeCrawler(new UniqueIdProvider(), store);
14 13
   });
15 14
 
16 15
   it('crawls a layout tree and adds unique id to each node', () => {

+ 14
- 14
src/commands/LayoutTreeParser.js View File

@@ -12,28 +12,28 @@ export default class LayoutTreeParser {
12 12
     if (simpleJsonApi.tabs) {
13 13
       return this._createTabs(simpleJsonApi.tabs);
14 14
     }
15
-    return this._createContainerStackWithContainer(simpleJsonApi.container);
15
+    return this._createContainerStackWithContainerData(simpleJsonApi.container);
16 16
   }
17 17
 
18
-  _createTabs(tabs) {
18
+  createContainer(data) {
19 19
     return {
20
-      type: LayoutTypes.Tabs,
21
-      children: _.map(tabs, (t) => this._createContainerStackWithContainer(t.container))
20
+      type: LayoutTypes.Container,
21
+      data,
22
+      children: []
22 23
     };
23 24
   }
24 25
 
25
-  _createContainerStackWithContainer(container) {
26
+  _createTabs(tabs) {
26 27
     return {
27
-      type: LayoutTypes.ContainerStack,
28
-      children: [this._createContainer(container)]
28
+      type: LayoutTypes.Tabs,
29
+      children: _.map(tabs, (t) => this._createContainerStackWithContainerData(t.container))
29 30
     };
30 31
   }
31 32
 
32
-  _createContainer(container) {
33
+  _createContainerStackWithContainerData(containerData) {
33 34
     return {
34
-      type: LayoutTypes.Container,
35
-      data: container,
36
-      children: []
35
+      type: LayoutTypes.ContainerStack,
36
+      children: [this.createContainer(containerData)]
37 37
     };
38 38
   }
39 39
 
@@ -49,19 +49,19 @@ export default class LayoutTreeParser {
49 49
     if (layout.sideMenu.left) {
50 50
       children.push({
51 51
         type: LayoutTypes.SideMenuLeft,
52
-        children: [this._createContainer(layout.sideMenu.left.container)]
52
+        children: [this.createContainer(layout.sideMenu.left.container)]
53 53
       });
54 54
     }
55 55
     children.push({
56 56
       type: LayoutTypes.SideMenuCenter,
57 57
       children: [
58
-        layout.tabs ? this._createTabs(layout.tabs) : this._createContainerStackWithContainer(layout.container)
58
+        layout.tabs ? this._createTabs(layout.tabs) : this._createContainerStackWithContainerData(layout.container)
59 59
       ]
60 60
     });
61 61
     if (layout.sideMenu.right) {
62 62
       children.push({
63 63
         type: LayoutTypes.SideMenuRight,
64
-        children: [this._createContainer(layout.sideMenu.right.container)]
64
+        children: [this.createContainer(layout.sideMenu.right.container)]
65 65
       });
66 66
     }
67 67
     return children;

+ 272
- 264
src/commands/LayoutTreeParser.test.js View File

@@ -1,287 +1,295 @@
1 1
 import * as SimpleLayouts from './SimpleLayouts';
2
+import LayoutTreeParser from './LayoutTreeParser';
2 3
 
3 4
 describe('LayoutTreeParser', () => {
4 5
   let uut;
5 6
 
6 7
   beforeEach(() => {
7
-    const LayoutTreeParser = require('./LayoutTreeParser').default;
8 8
     uut = new LayoutTreeParser();
9 9
   });
10 10
 
11
-  it('parses single screen', () => {
12
-    expect(uut.parseFromSimpleJSON(SimpleLayouts.singleScreenApp))
13
-      .toEqual({
14
-        type: 'ContainerStack',
15
-        children: [
16
-          {
17
-            type: 'Container',
18
-            data: {
19
-              name: 'com.example.MyScreen'
20
-            },
21
-            children: []
22
-          }
23
-        ]
24
-      });
25
-  });
11
+  describe('parseFromSimpleJSON', () => {
12
+    it('parses single screen', () => {
13
+      expect(uut.parseFromSimpleJSON(SimpleLayouts.singleScreenApp))
14
+        .toEqual({
15
+          type: 'ContainerStack',
16
+          children: [
17
+            {
18
+              type: 'Container',
19
+              data: {
20
+                name: 'com.example.MyScreen'
21
+              },
22
+              children: []
23
+            }
24
+          ]
25
+        });
26
+    });
26 27
 
27
-  it('parses single screen with props', () => {
28
-    expect(uut.parseFromSimpleJSON(SimpleLayouts.singleScreenWithAditionalParams))
29
-      .toEqual({
30
-        type: 'ContainerStack',
31
-        children: [
32
-          {
33
-            type: 'Container',
34
-            children: [],
35
-            data: {
36
-              name: 'com.example.MyScreen',
37
-              passProps: SimpleLayouts.passProps,
38
-              style: {},
39
-              buttons: {}
28
+    it('parses single screen with props', () => {
29
+      expect(uut.parseFromSimpleJSON(SimpleLayouts.singleScreenWithAditionalParams))
30
+        .toEqual({
31
+          type: 'ContainerStack',
32
+          children: [
33
+            {
34
+              type: 'Container',
35
+              children: [],
36
+              data: {
37
+                name: 'com.example.MyScreen',
38
+                passProps: SimpleLayouts.passProps,
39
+                style: {},
40
+                buttons: {}
41
+              }
40 42
             }
41
-          }
42
-        ]
43
-      });
44
-    const parsedPropsFn = uut.parseFromSimpleJSON(SimpleLayouts.singleScreenWithAditionalParams)
45
-      .children[0].data.passProps.fnProp;
46
-    expect(parsedPropsFn).toBe(SimpleLayouts.passProps.fnProp);
47
-    expect(parsedPropsFn()).toEqual('Hello from a function');
48
-  });
43
+          ]
44
+        });
45
+      const parsedPropsFn = uut.parseFromSimpleJSON(SimpleLayouts.singleScreenWithAditionalParams)
46
+        .children[0].data.passProps.fnProp;
47
+      expect(parsedPropsFn).toBe(SimpleLayouts.passProps.fnProp);
48
+      expect(parsedPropsFn()).toEqual('Hello from a function');
49
+    });
49 50
 
50
-  it('parses tab based', () => {
51
-    expect(uut.parseFromSimpleJSON(SimpleLayouts.tabBasedApp))
52
-      .toEqual({
53
-        type: 'Tabs',
54
-        children: [
55
-          {
56
-            type: 'ContainerStack',
57
-            children: [
58
-              {
59
-                type: 'Container',
60
-                children: [],
61
-                data: {
62
-                  name: 'com.example.ATab'
51
+    it('parses tab based', () => {
52
+      expect(uut.parseFromSimpleJSON(SimpleLayouts.tabBasedApp))
53
+        .toEqual({
54
+          type: 'Tabs',
55
+          children: [
56
+            {
57
+              type: 'ContainerStack',
58
+              children: [
59
+                {
60
+                  type: 'Container',
61
+                  children: [],
62
+                  data: {
63
+                    name: 'com.example.ATab'
64
+                  }
63 65
                 }
64
-              }
65
-            ]
66
-          },
67
-          {
68
-            type: 'ContainerStack',
69
-            children: [
70
-              {
71
-                type: 'Container',
72
-                children: [],
73
-                data: {
74
-                  name: 'com.example.SecondTab'
66
+              ]
67
+            },
68
+            {
69
+              type: 'ContainerStack',
70
+              children: [
71
+                {
72
+                  type: 'Container',
73
+                  children: [],
74
+                  data: {
75
+                    name: 'com.example.SecondTab'
76
+                  }
75 77
                 }
76
-              }
77
-            ]
78
-          },
79
-          {
80
-            type: 'ContainerStack',
81
-            children: [
82
-              {
83
-                type: 'Container',
84
-                children: [],
85
-                data: {
86
-                  name: 'com.example.ATab'
78
+              ]
79
+            },
80
+            {
81
+              type: 'ContainerStack',
82
+              children: [
83
+                {
84
+                  type: 'Container',
85
+                  children: [],
86
+                  data: {
87
+                    name: 'com.example.ATab'
88
+                  }
87 89
                 }
88
-              }
89
-            ]
90
-          }
91
-        ]
92
-      });
93
-  });
90
+              ]
91
+            }
92
+          ]
93
+        });
94
+    });
94 95
 
95
-  it('parses side menus', () => {
96
-    expect(uut.parseFromSimpleJSON(SimpleLayouts.singleWithSideMenu))
97
-      .toEqual({
98
-        type: 'SideMenuRoot',
99
-        children: [
100
-          {
101
-            type: 'SideMenuLeft',
102
-            children: [
103
-              {
104
-                type: 'Container',
105
-                data: {
106
-                  name: 'com.example.SideMenu'
107
-                },
108
-                children: []
109
-              }
110
-            ]
111
-          },
112
-          {
113
-            type: 'SideMenuCenter',
114
-            children: [
115
-              {
116
-                type: 'ContainerStack',
117
-                children: [
118
-                  {
119
-                    type: 'Container',
120
-                    data: {
121
-                      name: 'com.example.MyScreen'
122
-                    },
123
-                    children: []
124
-                  }
125
-                ]
126
-              }
127
-            ]
128
-          }
129
-        ]
130
-      });
131
-  });
96
+    it('parses side menus', () => {
97
+      expect(uut.parseFromSimpleJSON(SimpleLayouts.singleWithSideMenu))
98
+        .toEqual({
99
+          type: 'SideMenuRoot',
100
+          children: [
101
+            {
102
+              type: 'SideMenuLeft',
103
+              children: [
104
+                {
105
+                  type: 'Container',
106
+                  data: {
107
+                    name: 'com.example.SideMenu'
108
+                  },
109
+                  children: []
110
+                }
111
+              ]
112
+            },
113
+            {
114
+              type: 'SideMenuCenter',
115
+              children: [
116
+                {
117
+                  type: 'ContainerStack',
118
+                  children: [
119
+                    {
120
+                      type: 'Container',
121
+                      data: {
122
+                        name: 'com.example.MyScreen'
123
+                      },
124
+                      children: []
125
+                    }
126
+                  ]
127
+                }
128
+              ]
129
+            }
130
+          ]
131
+        });
132
+    });
132 133
 
133
-  it('parses side menu right', () => {
134
-    expect(uut.parseFromSimpleJSON(SimpleLayouts.singleWithRightSideMenu))
135
-      .toEqual({
136
-        type: 'SideMenuRoot',
137
-        children: [
138
-          {
139
-            type: 'SideMenuCenter',
140
-            children: [
141
-              {
142
-                type: 'ContainerStack',
143
-                children: [
144
-                  {
145
-                    type: 'Container',
146
-                    data: {
147
-                      name: 'com.example.MyScreen'
148
-                    },
149
-                    children: []
150
-                  }
151
-                ]
152
-              }
153
-            ]
154
-          },
155
-          {
156
-            type: 'SideMenuRight',
157
-            children: [
158
-              {
159
-                type: 'Container',
160
-                data: {
161
-                  name: 'com.example.SideMenu'
162
-                },
163
-                children: []
164
-              }
165
-            ]
166
-          }
167
-        ]
168
-      });
169
-  });
134
+    it('parses side menu right', () => {
135
+      expect(uut.parseFromSimpleJSON(SimpleLayouts.singleWithRightSideMenu))
136
+        .toEqual({
137
+          type: 'SideMenuRoot',
138
+          children: [
139
+            {
140
+              type: 'SideMenuCenter',
141
+              children: [
142
+                {
143
+                  type: 'ContainerStack',
144
+                  children: [
145
+                    {
146
+                      type: 'Container',
147
+                      data: {
148
+                        name: 'com.example.MyScreen'
149
+                      },
150
+                      children: []
151
+                    }
152
+                  ]
153
+                }
154
+              ]
155
+            },
156
+            {
157
+              type: 'SideMenuRight',
158
+              children: [
159
+                {
160
+                  type: 'Container',
161
+                  data: {
162
+                    name: 'com.example.SideMenu'
163
+                  },
164
+                  children: []
165
+                }
166
+              ]
167
+            }
168
+          ]
169
+        });
170
+    });
170 171
 
171
-  it('parses both side menus', () => {
172
-    expect(uut.parseFromSimpleJSON(SimpleLayouts.singleWithBothMenus))
173
-      .toEqual({
174
-        type: 'SideMenuRoot',
175
-        children: [
176
-          {
177
-            type: 'SideMenuLeft',
178
-            children: [
179
-              {
180
-                type: 'Container',
181
-                data: {
182
-                  name: 'com.example.Menu1'
183
-                },
184
-                children: []
185
-              }
186
-            ]
187
-          },
188
-          {
189
-            type: 'SideMenuCenter',
190
-            children: [
191
-              {
192
-                type: 'ContainerStack',
193
-                children: [
194
-                  {
195
-                    type: 'Container',
196
-                    data: {
197
-                      name: 'com.example.MyScreen'
172
+    it('parses both side menus', () => {
173
+      expect(uut.parseFromSimpleJSON(SimpleLayouts.singleWithBothMenus))
174
+        .toEqual({
175
+          type: 'SideMenuRoot',
176
+          children: [
177
+            {
178
+              type: 'SideMenuLeft',
179
+              children: [
180
+                {
181
+                  type: 'Container',
182
+                  data: {
183
+                    name: 'com.example.Menu1'
184
+                  },
185
+                  children: []
186
+                }
187
+              ]
188
+            },
189
+            {
190
+              type: 'SideMenuCenter',
191
+              children: [
192
+                {
193
+                  type: 'ContainerStack',
194
+                  children: [
195
+                    {
196
+                      type: 'Container',
197
+                      data: {
198
+                        name: 'com.example.MyScreen'
199
+                      },
200
+                      children: []
201
+                    }
202
+                  ]
203
+                }
204
+              ]
205
+            },
206
+            {
207
+              type: 'SideMenuRight',
208
+              children: [
209
+                {
210
+                  type: 'Container',
211
+                  data: {
212
+                    name: 'com.example.Menu2'
213
+                  },
214
+                  children: []
215
+                }
216
+              ]
217
+            }
218
+          ]
219
+        });
220
+    });
221
+
222
+    it('parses tabs with side menus', () => {
223
+      expect(uut.parseFromSimpleJSON(SimpleLayouts.tabBasedWithBothSideMenus))
224
+        .toEqual({
225
+          type: 'SideMenuRoot',
226
+          children: [
227
+            {
228
+              type: 'SideMenuLeft',
229
+              children: [
230
+                {
231
+                  type: 'Container',
232
+                  data: {
233
+                    name: 'com.example.Menu1'
234
+                  },
235
+                  children: []
236
+                }
237
+              ]
238
+            },
239
+            {
240
+              type: 'SideMenuCenter',
241
+              children: [
242
+                {
243
+                  type: 'Tabs',
244
+                  children: [
245
+                    {
246
+                      type: 'ContainerStack',
247
+                      children: [
248
+                        {
249
+                          type: 'Container',
250
+                          data: {
251
+                            name: 'com.example.FirstTab'
252
+                          },
253
+                          children: []
254
+                        }
255
+                      ]
198 256
                     },
199
-                    children: []
200
-                  }
201
-                ]
202
-              }
203
-            ]
204
-          },
205
-          {
206
-            type: 'SideMenuRight',
207
-            children: [
208
-              {
209
-                type: 'Container',
210
-                data: {
211
-                  name: 'com.example.Menu2'
212
-                },
213
-                children: []
214
-              }
215
-            ]
216
-          }
217
-        ]
218
-      });
257
+                    {
258
+                      type: 'ContainerStack',
259
+                      children: [
260
+                        {
261
+                          type: 'Container',
262
+                          data: {
263
+                            name: 'com.example.SecondTab'
264
+                          },
265
+                          children: []
266
+                        }
267
+                      ]
268
+                    }
269
+                  ]
270
+                }
271
+              ]
272
+            },
273
+            {
274
+              type: 'SideMenuRight',
275
+              children: [
276
+                {
277
+                  type: 'Container',
278
+                  data: {
279
+                    name: 'com.example.Menu2'
280
+                  },
281
+                  children: []
282
+                }
283
+              ]
284
+            }
285
+          ]
286
+        });
287
+    });
219 288
   });
220 289
 
221
-  it('parses tabs with side menus', () => {
222
-    expect(uut.parseFromSimpleJSON(SimpleLayouts.tabBasedWithBothSideMenus))
223
-      .toEqual({
224
-        type: 'SideMenuRoot',
225
-        children: [
226
-          {
227
-            type: 'SideMenuLeft',
228
-            children: [
229
-              {
230
-                type: 'Container',
231
-                data: {
232
-                  name: 'com.example.Menu1'
233
-                },
234
-                children: []
235
-              }
236
-            ]
237
-          },
238
-          {
239
-            type: 'SideMenuCenter',
240
-            children: [
241
-              {
242
-                type: 'Tabs',
243
-                children: [
244
-                  {
245
-                    type: 'ContainerStack',
246
-                    children: [
247
-                      {
248
-                        type: 'Container',
249
-                        data: {
250
-                          name: 'com.example.FirstTab'
251
-                        },
252
-                        children: []
253
-                      }
254
-                    ]
255
-                  },
256
-                  {
257
-                    type: 'ContainerStack',
258
-                    children: [
259
-                      {
260
-                        type: 'Container',
261
-                        data: {
262
-                          name: 'com.example.SecondTab'
263
-                        },
264
-                        children: []
265
-                      }
266
-                    ]
267
-                  }
268
-                ]
269
-              }
270
-            ]
271
-          },
272
-          {
273
-            type: 'SideMenuRight',
274
-            children: [
275
-              {
276
-                type: 'Container',
277
-                data: {
278
-                  name: 'com.example.Menu2'
279
-                },
280
-                children: []
281
-              }
282
-            ]
283
-          }
284
-        ]
285
-      });
290
+  describe('createContainer', () => {
291
+    it('creates container object with passed data', () => {
292
+      expect(uut.createContainer({ foo: 'bar' })).toEqual({ type: 'Container', data: { foo: 'bar' }, children: [] });
293
+    });
286 294
   });
287 295
 });

+ 3
- 2
src/containers/ContainerRegistry.test.js View File

@@ -2,6 +2,9 @@ import React, { Component } from 'react';
2 2
 import { AppRegistry, Text } from 'react-native';
3 3
 import renderer from 'react-test-renderer';
4 4
 
5
+import ContainerRegistry from './ContainerRegistry';
6
+import Store from './Store';
7
+
5 8
 describe('ContainerRegistry', () => {
6 9
   let uut;
7 10
 
@@ -13,8 +16,6 @@ describe('ContainerRegistry', () => {
13 16
 
14 17
   beforeEach(() => {
15 18
     AppRegistry.registerComponent = jest.fn(AppRegistry.registerComponent);
16
-    const ContainerRegistry = require('./ContainerRegistry').default;
17
-    const Store = require('./Store').default;
18 19
     uut = new ContainerRegistry(new Store());
19 20
   });
20 21
 

+ 3
- 3
src/containers/ContainerWrapper.test.js View File

@@ -2,8 +2,10 @@ import React, { Component } from 'react';
2 2
 import { AppRegistry, Text } from 'react-native';
3 3
 import renderer from 'react-test-renderer';
4 4
 
5
+import ContainerWrapper from './ContainerWrapper';
6
+import Store from './Store';
7
+
5 8
 describe('ContainerWrapper', () => {
6
-  let ContainerWrapper;
7 9
   let myContainerRef;
8 10
   let testParentRef;
9 11
   let store;
@@ -37,8 +39,6 @@ describe('ContainerWrapper', () => {
37 39
   }
38 40
 
39 41
   beforeEach(() => {
40
-    ContainerWrapper = require('./ContainerWrapper').default;
41
-    const Store = require('./Store').default;
42 42
     store = new Store();
43 43
   });
44 44
 

+ 2
- 1
src/containers/Store.test.js View File

@@ -1,8 +1,9 @@
1
+import Store from './Store';
2
+
1 3
 describe('Store', () => {
2 4
   let uut;
3 5
 
4 6
   beforeEach(() => {
5
-    const Store = require('./Store').default;
6 7
     uut = new Store();
7 8
   });
8 9