From ee04610f6a9c9117f9ae8c17ae6d9ce9ca132883 Mon Sep 17 00:00:00 2001 From: Henrik Raitasola Date: Mon, 24 Dec 2018 10:25:56 +0200 Subject: [PATCH] Refactor options processor v2 (#4492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Goal of this PR The goal of this PR is to make `OptionsProcessor.ts` great again 🇺🇸 . To me it was total mess including tests. ## What was wrong or funky? 1. the tests tested if React Native's `processColor` color works as it should even though react native has [multiple tests for this](https://github.com/facebook/react-native/blob/master/Libraries/StyleSheet/__tests__/processColor-test.js). Aka we don't have to test React Native's functions :D 2. `LayoutTreeCrawler.test.ts` was testing `OptionsProcessor`s functionality so it was removed now 3. Lots of small things since this was done before putting `noImplicitAny` to true so it wasn't made TypeScript in mind ## Result Now `OptionsProcessor.ts` should be 1. code way more clear 2. tests are testing only things that they should 3. tests are written in clear way 4. it is easy to see from the tests what the whole function does 5. type safe for TypeScript `noImplicitAny` (when we actually turn it on) --- lib/src/Navigation.ts | 15 +- lib/src/adapters/AssetResolver.ts | 8 + lib/src/adapters/ColorService.ts | 7 + lib/src/commands/Commands.test.ts | 20 +- lib/src/commands/Commands.ts | 10 +- lib/src/commands/LayoutTreeCrawler.test.ts | 88 +------- lib/src/commands/LayoutTreeCrawler.ts | 18 +- lib/src/commands/OptionsProcessor.test.ts | 241 +++++++-------------- lib/src/commands/OptionsProcessor.ts | 45 ++-- 9 files changed, 175 insertions(+), 277 deletions(-) create mode 100644 lib/src/adapters/AssetResolver.ts create mode 100644 lib/src/adapters/ColorService.ts diff --git a/lib/src/Navigation.ts b/lib/src/Navigation.ts index 6794793e678..56b46a958b6 100644 --- a/lib/src/Navigation.ts +++ b/lib/src/Navigation.ts @@ -17,6 +17,9 @@ import { TouchablePreview } from './adapters/TouchablePreview'; import { LayoutRoot, Layout } from './interfaces/Layout'; import { Options } from './interfaces/Options'; import { ComponentWrapper } from './components/ComponentWrapper'; +import { OptionsProcessor } from './commands/OptionsProcessor'; +import { ColorService } from './adapters/ColorService'; +import { AssetService } from './adapters/AssetResolver'; export class NavigationRoot { public readonly Element: React.ComponentType<{ elementId: any; resizeMode?: any; }>; @@ -44,10 +47,18 @@ export class NavigationRoot { this.componentEventsObserver = new ComponentEventsObserver(this.nativeEventsReceiver); this.componentRegistry = new ComponentRegistry(this.store, this.componentEventsObserver); this.layoutTreeParser = new LayoutTreeParser(); - this.layoutTreeCrawler = new LayoutTreeCrawler(this.uniqueIdProvider, this.store); + const optionsProcessor = new OptionsProcessor(this.store, this.uniqueIdProvider, new ColorService(), new AssetService()); + this.layoutTreeCrawler = new LayoutTreeCrawler(this.uniqueIdProvider, this.store, optionsProcessor); this.nativeCommandsSender = new NativeCommandsSender(); this.commandsObserver = new CommandsObserver(); - this.commands = new Commands(this.nativeCommandsSender, this.layoutTreeParser, this.layoutTreeCrawler, this.commandsObserver, this.uniqueIdProvider); + this.commands = new Commands( + this.nativeCommandsSender, + this.layoutTreeParser, + this.layoutTreeCrawler, + this.commandsObserver, + this.uniqueIdProvider, + optionsProcessor + ); this.eventsRegistry = new EventsRegistry(this.nativeEventsReceiver, this.commandsObserver, this.componentEventsObserver); this.componentEventsObserver.registerOnceForAllComponentEvents(); diff --git a/lib/src/adapters/AssetResolver.ts b/lib/src/adapters/AssetResolver.ts new file mode 100644 index 00000000000..81a6f9cd61b --- /dev/null +++ b/lib/src/adapters/AssetResolver.ts @@ -0,0 +1,8 @@ +import * as resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; +import { ImageRequireSource } from 'react-native'; + +export class AssetService { + resolveFromRequire(value: ImageRequireSource) { + return resolveAssetSource(value); + } +} diff --git a/lib/src/adapters/ColorService.ts b/lib/src/adapters/ColorService.ts new file mode 100644 index 00000000000..69f06d4c16c --- /dev/null +++ b/lib/src/adapters/ColorService.ts @@ -0,0 +1,7 @@ +import { processColor } from 'react-native'; + +export class ColorService { + toNativeColor(inputColor: string) { + return processColor(inputColor); + } +} diff --git a/lib/src/commands/Commands.test.ts b/lib/src/commands/Commands.test.ts index 71a605b209d..a3298d323e7 100644 --- a/lib/src/commands/Commands.test.ts +++ b/lib/src/commands/Commands.test.ts @@ -8,6 +8,7 @@ import { UniqueIdProvider } from '../adapters/UniqueIdProvider.mock'; import { Commands } from './Commands'; import { CommandsObserver } from '../events/CommandsObserver'; import { NativeCommandsSender } from '../adapters/NativeCommandsSender'; +import { OptionsProcessor } from './OptionsProcessor'; describe('Commands', () => { let uut: Commands; @@ -22,12 +23,16 @@ describe('Commands', () => { mockedNativeCommandsSender = mock(NativeCommandsSender); nativeCommandsSender = instance(mockedNativeCommandsSender); + const mockedOptionsProcessor = mock(OptionsProcessor); + const optionsProcessor = instance(mockedOptionsProcessor); + uut = new Commands( nativeCommandsSender, new LayoutTreeParser(), - new LayoutTreeCrawler(new UniqueIdProvider(), store), + new LayoutTreeCrawler(new UniqueIdProvider(), store, optionsProcessor), commandsObserver, - new UniqueIdProvider() + new UniqueIdProvider(), + optionsProcessor ); }); @@ -353,7 +358,16 @@ describe('Commands', () => { const mockParser = { parse: () => 'parsed' }; const mockCrawler = { crawl: (x: any) => x, processOptions: (x: any) => x }; commandsObserver.register(cb); - uut = new Commands(mockedNativeCommandsSender, mockParser as any, mockCrawler as any, commandsObserver, new UniqueIdProvider()); + const mockedOptionsProcessor = mock(OptionsProcessor); + const optionsProcessor = instance(mockedOptionsProcessor); + uut = new Commands( + mockedNativeCommandsSender, + mockParser as any, + mockCrawler as any, + commandsObserver, + new UniqueIdProvider(), + optionsProcessor + ); }); function getAllMethodsOfUut() { diff --git a/lib/src/commands/Commands.ts b/lib/src/commands/Commands.ts index b106972fcdf..261c33b4c42 100644 --- a/lib/src/commands/Commands.ts +++ b/lib/src/commands/Commands.ts @@ -6,6 +6,7 @@ import { Options } from '../interfaces/Options'; import { Layout, LayoutRoot } from '../interfaces/Layout'; import { LayoutTreeParser } from './LayoutTreeParser'; import { LayoutTreeCrawler } from './LayoutTreeCrawler'; +import { OptionsProcessor } from './OptionsProcessor'; export class Commands { constructor( @@ -13,8 +14,9 @@ export class Commands { private readonly layoutTreeParser: LayoutTreeParser, private readonly layoutTreeCrawler: LayoutTreeCrawler, private readonly commandsObserver: CommandsObserver, - private readonly uniqueIdProvider: UniqueIdProvider) { - } + private readonly uniqueIdProvider: UniqueIdProvider, + private readonly optionsProcessor: OptionsProcessor + ) {} public setRoot(simpleApi: LayoutRoot) { const input = _.cloneDeep(simpleApi); @@ -41,7 +43,7 @@ export class Commands { public setDefaultOptions(options: Options) { const input = _.cloneDeep(options); - this.layoutTreeCrawler.processOptions(input); + this.optionsProcessor.processOptions(input); this.nativeCommandsSender.setDefaultOptions(input); this.commandsObserver.notify('setDefaultOptions', { options }); @@ -49,7 +51,7 @@ export class Commands { public mergeOptions(componentId: string, options: Options) { const input = _.cloneDeep(options); - this.layoutTreeCrawler.processOptions(input); + this.optionsProcessor.processOptions(input); this.nativeCommandsSender.mergeOptions(componentId, input); this.commandsObserver.notify('mergeOptions', { componentId, options }); diff --git a/lib/src/commands/LayoutTreeCrawler.test.ts b/lib/src/commands/LayoutTreeCrawler.test.ts index 7acc51d82ea..52f05760afa 100644 --- a/lib/src/commands/LayoutTreeCrawler.test.ts +++ b/lib/src/commands/LayoutTreeCrawler.test.ts @@ -4,6 +4,8 @@ import { LayoutType } from './LayoutType'; import { LayoutTreeCrawler, LayoutNode } from './LayoutTreeCrawler'; import { UniqueIdProvider } from '../adapters/UniqueIdProvider.mock'; import { Store } from '../components/Store'; +import { mock, instance } from 'ts-mockito'; +import { OptionsProcessor } from './OptionsProcessor'; describe('LayoutTreeCrawler', () => { let uut: LayoutTreeCrawler; @@ -11,7 +13,9 @@ describe('LayoutTreeCrawler', () => { beforeEach(() => { store = new Store(); - uut = new LayoutTreeCrawler(new UniqueIdProvider(), store); + const mockedOptionsProcessor = mock(OptionsProcessor); + const optionsProcessor = instance(mockedOptionsProcessor); + uut = new LayoutTreeCrawler(new UniqueIdProvider(), store, optionsProcessor); }); it('crawls a layout tree and adds unique id to each node', () => { @@ -202,88 +206,6 @@ describe('LayoutTreeCrawler', () => { expect(node.data.passProps).toBeUndefined(); }); - describe('navigation options', () => { - let options: Record; - let node: LayoutNode; - - beforeEach(() => { - options = {}; - node = { type: LayoutType.Component, data: { name: 'theComponentName', options }, children: [] }; - }); - - it('processes colors into numeric AARRGGBB', () => { - options.someKeyColor = 'red'; - uut.crawl(node); - expect(node.data.options.someKeyColor).toEqual(0xffff0000); - }); - - it('processes colors into numeric AARRGGBB', () => { - options.someKeyColor = 'yellow'; - uut.crawl(node); - expect(node.data.options.someKeyColor).toEqual(0xffffff00); - }); - - it('processes numeric colors', () => { - options.someKeyColor = '#123456'; - uut.crawl(node); - expect(node.data.options.someKeyColor).toEqual(0xff123456); - }); - - it('processes numeric colors with rrggbbAA', () => { - options.someKeyColor = 0x123456ff; // wut - uut.crawl(node); - expect(node.data.options.someKeyColor).toEqual(0xff123456); - }); - - it('process colors with rgb functions', () => { - options.someKeyColor = 'rgb(255, 0, 255)'; - uut.crawl(node); - expect(node.data.options.someKeyColor).toEqual(0xffff00ff); - }); - - it('process colors with special words', () => { - options.someKeyColor = 'fuchsia'; - uut.crawl(node); - expect(node.data.options.someKeyColor).toEqual(0xffff00ff); - }); - - it('process colors with hsla functions', () => { - options.someKeyColor = 'hsla(360, 100%, 100%, 1.0)'; - uut.crawl(node); - expect(node.data.options.someKeyColor).toEqual(0xffffffff); - }); - - it('unknown colors return undefined', () => { - options.someKeyColor = 'wut'; - uut.crawl(node); - expect(node.data.options.someKeyColor).toEqual(undefined); - }); - - it('any keys ending with Color', () => { - options.otherKeyColor = 'red'; - options.yetAnotherColor = 'blue'; - options.andAnotherColor = 'rgb(0, 255, 0)'; - uut.crawl(node); - expect(node.data.options.otherKeyColor).toEqual(0xffff0000); - expect(node.data.options.yetAnotherColor).toEqual(0xff0000ff); - expect(node.data.options.andAnotherColor).toEqual(0xff00ff00); - }); - - it('keys ending with Color case sensitive', () => { - options.otherKey_color = 'red'; // eslint-disable-line camelcase - uut.crawl(node); - expect(node.data.options.otherKey_color).toEqual('red'); - }); - - it('any nested recursive keys ending with Color', () => { - options.innerObj = { theKeyColor: 'red' }; - options.innerObj.innerMostObj = { anotherColor: 'yellow' }; - uut.crawl(node); - expect(node.data.options.innerObj.theKeyColor).toEqual(0xffff0000); - expect(node.data.options.innerObj.innerMostObj.anotherColor).toEqual(0xffffff00); - }); - }); - describe('LayoutNode', () => { it('convertable from same data structure', () => { const x = { diff --git a/lib/src/commands/LayoutTreeCrawler.ts b/lib/src/commands/LayoutTreeCrawler.ts index b84250a1742..0ae1dfbb23d 100644 --- a/lib/src/commands/LayoutTreeCrawler.ts +++ b/lib/src/commands/LayoutTreeCrawler.ts @@ -1,6 +1,7 @@ import * as _ from 'lodash'; -import { OptionsProcessor } from './OptionsProcessor'; import { LayoutType } from './LayoutType'; +import { OptionsProcessor } from './OptionsProcessor'; +import { UniqueIdProvider } from '../adapters/UniqueIdProvider'; export interface Data { name?: string; @@ -15,13 +16,12 @@ export interface LayoutNode { } export class LayoutTreeCrawler { - private optionsProcessor: OptionsProcessor; constructor( - private readonly uniqueIdProvider: any, - public readonly store: any) { + private readonly uniqueIdProvider: UniqueIdProvider, + public readonly store: any, + private readonly optionsProcessor: OptionsProcessor + ) { this.crawl = this.crawl.bind(this); - this.processOptions = this.processOptions.bind(this); - this.optionsProcessor = new OptionsProcessor(store, uniqueIdProvider); } crawl(node: LayoutNode): void { @@ -29,14 +29,10 @@ export class LayoutTreeCrawler { if (node.type === LayoutType.Component) { this._handleComponent(node); } - this.processOptions(node.data.options); + this.optionsProcessor.processOptions(node.data.options); _.forEach(node.children, this.crawl); } - processOptions(options) { - this.optionsProcessor.processOptions(options); - } - _handleComponent(node) { this._assertComponentDataName(node); this._savePropsToStore(node); diff --git a/lib/src/commands/OptionsProcessor.test.ts b/lib/src/commands/OptionsProcessor.test.ts index 766fdca3ef5..ebd9011bf5b 100644 --- a/lib/src/commands/OptionsProcessor.test.ts +++ b/lib/src/commands/OptionsProcessor.test.ts @@ -2,209 +2,130 @@ import { OptionsProcessor } from './OptionsProcessor'; import { UniqueIdProvider } from '../adapters/UniqueIdProvider'; import { Store } from '../components/Store'; import * as _ from 'lodash'; +import { Options, OptionsModalPresentationStyle } from '../interfaces/Options'; +import { mock, when, anyString, instance, anyNumber, verify } from 'ts-mockito'; +import { ColorService } from '../adapters/ColorService'; +import { AssetService } from '../adapters/AssetResolver'; describe('navigation options', () => { let uut: OptionsProcessor; - let options: Record; - let store: Store; + const mockedStore = mock(Store); + const store = instance(mockedStore); + beforeEach(() => { - options = {}; - store = new Store(); - uut = new OptionsProcessor(store, new UniqueIdProvider()); - }); + const mockedAssetService = mock(AssetService); + when(mockedAssetService.resolveFromRequire(anyNumber())).thenReturn('lol'); + const assetService = instance(mockedAssetService); - it('processes colors into numeric AARRGGBB', () => { - options.someKeyColor = 'red'; - options.color = 'blue'; - uut.processOptions(options); - expect(options.someKeyColor).toEqual(0xffff0000); - expect(options.color).toEqual(0xff0000ff); + const mockedColorService = mock(ColorService); + when(mockedColorService.toNativeColor(anyString())).thenReturn(666); + const colorService = instance(mockedColorService); - options.someKeyColor = 'yellow'; - uut.processOptions(options); - expect(options.someKeyColor).toEqual(0xffffff00); + uut = new OptionsProcessor(store, new UniqueIdProvider(), colorService, assetService); }); - it('processes numeric colors', () => { - options.someKeyColor = '#123456'; + it('keeps original values if values were not processed', () => { + const options: Options = { + blurOnUnmount: false, + popGesture: false, + modalPresentationStyle: OptionsModalPresentationStyle.fullScreen, + animations: { dismissModal: { alpha: { from: 0, to: 1 } } }, + }; uut.processOptions(options); - expect(options.someKeyColor).toEqual(0xff123456); - - options.someKeyColor = 0x123456ff; // wut + expect(options).toEqual({ + blurOnUnmount: false, + popGesture: false, + modalPresentationStyle: OptionsModalPresentationStyle.fullScreen, + animations: { dismissModal: { alpha: { from: 0, to: 1 } } }, + }); + }); + + it('processes color keys', () => { + const options: Options = { + statusBar: { backgroundColor: 'red' }, + topBar: { background: { color: 'blue' } }, + }; uut.processOptions(options); - expect(options.someKeyColor).toEqual(0xff123456); + expect(options).toEqual({ + statusBar: { backgroundColor: 666 }, + topBar: { background: { color: 666 } }, + }); }); - it('process colors with rgb functions', () => { - options.someKeyColor = 'rgb(255, 0, 255)'; + it('processes image keys', () => { + const options: Options = { + backgroundImage: 123, + rootBackgroundImage: 234, + bottomTab: { icon: 345, selectedIcon: 345 }, + }; uut.processOptions(options); - expect(options.someKeyColor).toEqual(0xffff00ff); + expect(options).toEqual({ + backgroundImage: 'lol', + rootBackgroundImage: 'lol', + bottomTab: { icon: 'lol', selectedIcon: 'lol' }, + }); }); - it('process colors with special words', () => { - options.someKeyColor = 'fuchsia'; - uut.processOptions(options); - expect(options.someKeyColor).toEqual(0xffff00ff); - }); + it('calls store if component has passProps', () => { + const passProps = { some: 'thing' }; + const options = { topBar: { title: { component: { passProps, name: 'a' } } } }; - it('process colors with hsla functions', () => { - options.someKeyColor = 'hsla(360, 100%, 100%, 1.0)'; uut.processOptions(options); - expect(options.someKeyColor).toEqual(0xffffffff); + verify(mockedStore.setPropsForId('CustomComponent1', passProps)).called(); }); - it('unknown colors return undefined', () => { - options.someKeyColor = 'wut'; - uut.processOptions(options); - expect(options.someKeyColor).toEqual(undefined); - }); + it('generates componentId for component id was not passed', () => { + const options = { topBar: { title: { component: { name: 'a' } } } }; - it('any keys ending with Color', () => { - options.otherKeyColor = 'red'; - options.yetAnotherColor = 'blue'; - options.andAnotherColor = 'rgb(0, 255, 0)'; uut.processOptions(options); - expect(options.otherKeyColor).toEqual(0xffff0000); - expect(options.yetAnotherColor).toEqual(0xff0000ff); - expect(options.andAnotherColor).toEqual(0xff00ff00); - }); - it('keys ending with Color case sensitive', () => { - options.otherKey_color = 'red'; // eslint-disable-line camelcase - uut.processOptions(options); - expect(options.otherKey_color).toEqual('red'); + expect(options).toEqual({ + topBar: { title: { component: { name: 'a', componentId: 'CustomComponent1' } } }, + }); }); - it('any nested recursive keys ending with Color', () => { - options.topBar = { textColor: 'red' }; - options.topBar.innerMostObj = { anotherColor: 'yellow' }; - uut.processOptions(options); - expect(options.topBar.textColor).toEqual(0xffff0000); - expect(options.topBar.innerMostObj.anotherColor).toEqual(0xffffff00); - }); + it('copies passed id to componentId key', () => { + const options = { topBar: { title: { component: { name: 'a', id: 'Component1' } } } }; - it('resolve image sources with name/ending with icon', () => { - options.icon = 'require("https://wix.github.io/react-native-navigation/_images/logo.png");'; - options.image = 'require("https://wix.github.io/react-native-navigation/_images/logo.png");'; - options.myImage = 'require("https://wix.github.io/react-native-navigation/_images/logo.png");'; - options.topBar = { - myIcon: 'require("https://wix.github.io/react-native-navigation/_images/logo.png");', - myOtherValue: 'value' - }; uut.processOptions(options); - // As we can't import external images and we don't want to add an image here - // I assign the icons to strings (what the require would generally look like) - // and expect the value to be resolved, in this case it doesn't find anything and returns null - expect(options.icon).toEqual(null); - expect(options.topBar.myIcon).toEqual(null); - expect(options.image).toEqual(null); - expect(options.myImage).toEqual(null); - expect(options.topBar.myOtherValue).toEqual('value'); + expect(options).toEqual({ + topBar: { title: { component: { name: 'a', id: 'Component1', componentId: 'Component1' } } }, + }); }); - it('passProps for Buttons options', () => { + it('calls store when button has passProps and id', () => { const passProps = { prop: 'prop' }; - options.rightButtons = [{ passProps, id: '1' }]; - - uut.processOptions({ o: options }); - - expect(store.getPropsForId('1')).toEqual(passProps); - }); - - it('passProps for custom component', () => { - const passProps = { color: '#ff0000', some: 'thing' }; - options.component = { passProps, name: 'a' }; - - uut.processOptions({ o: options }); - - expect(store.getPropsForId(options.component.componentId)).toEqual(passProps); - expect(Object.keys(options.component)).not.toContain('passProps'); - }); - - it('generate component id for component in options', () => { - options.component = { name: 'a' }; - - uut.processOptions({ o: options }); - - expect(options.component.componentId).toBeDefined(); - }); - - it('passProps from options are not processed', () => { - const passProps = { color: '#ff0000', some: 'thing' }; - const clonedProps = _.cloneDeep(passProps); - options.component = { passProps, name: 'a' }; + const options = { topBar: { rightButtons: [{ passProps, id: '1' }] } }; uut.processOptions(options); - expect(store.getPropsForId(options.component.componentId)).toEqual(clonedProps); - }); - it('pass supplied componentId for component in options', () => { - options.component = { name: 'a', id: 'Component1' }; - - uut.processOptions({ o: options }); - - expect(options.component.componentId).toEqual('Component1'); + verify(mockedStore.setPropsForId('1', passProps)).called(); }); - it('passProps must be with id next to it', () => { + it('do not touch passProps when id for button is missing', () => { const passProps = { prop: 'prop' }; - options.rightButtons = [{ passProps }]; - - uut.processOptions({ o: options }); - - expect(store.getPropsForId('1')).toEqual({}); - }); + const options = { topBar: { rightButtons: [{ passProps } as any] } }; - it('processes Options object', () => { - options.someKeyColor = 'rgb(255, 0, 255)'; - options.topBar = { textColor: 'red' }; - options.topBar.innerMostObj = { anotherColor: 'yellow' }; - - uut.processOptions({ o: options }); - - expect(options.topBar.textColor).toEqual(0xffff0000); - }); - - it('undefined value return undefined ', () => { - options.someImage = undefined; uut.processOptions(options); - expect(options.someImage).toEqual(undefined); + expect(options).toEqual({ topBar: { rightButtons: [{ passProps }] } }); }); - it('omits passProps when processing options', () => { - const passProps = { + it('omits passProps when processing buttons or components', () => { + const options = { topBar: { - rightButtons: [ - { - passProps: {}, - id: 'btn1' - }, - ], - leftButtons: [ - { - passProps: {}, - id: 'btn2' - } - ], - title: { - component: { - passProps: {} - } - }, - background: { - component: { - passProps: {} - } - } - } + rightButtons: [{ passProps: {}, id: 'btn1' }], + leftButtons: [{ passProps: {}, id: 'btn2' }], + title: { component: { name: 'helloThere1', passProps: {} } }, + background: { component: { name: 'helloThere2', passProps: {} } }, + }, }; - uut.processOptions(passProps); - expect(passProps.topBar.rightButtons[0].passProps).toBeUndefined(); - expect(passProps.topBar.leftButtons[0].passProps).toBeUndefined(); - expect(passProps.topBar.title.component.passProps).toBeUndefined(); - expect(passProps.topBar.background.component.passProps).toBeUndefined(); + uut.processOptions(options); + expect(options.topBar.rightButtons[0].passProps).toBeUndefined(); + expect(options.topBar.leftButtons[0].passProps).toBeUndefined(); + expect(options.topBar.title.component.passProps).toBeUndefined(); + expect(options.topBar.background.component.passProps).toBeUndefined(); }); }); diff --git a/lib/src/commands/OptionsProcessor.ts b/lib/src/commands/OptionsProcessor.ts index 1611f592a11..cf759a5ab3f 100644 --- a/lib/src/commands/OptionsProcessor.ts +++ b/lib/src/commands/OptionsProcessor.ts @@ -1,37 +1,54 @@ import * as _ from 'lodash'; -import { processColor } from 'react-native'; -import * as resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; import { Store } from '../components/Store'; import { UniqueIdProvider } from '../adapters/UniqueIdProvider'; +import { ColorService } from '../adapters/ColorService'; +import { AssetService } from '../adapters/AssetResolver'; +import { Options } from '../interfaces/Options'; export class OptionsProcessor { - constructor(public store: Store, public uniqueIdProvider: UniqueIdProvider) { } + constructor( + private store: Store, + private uniqueIdProvider: UniqueIdProvider, + private colorService: ColorService, + private assetService: AssetService, + ) {} - public processOptions(options: Record) { - _.forEach(options, (value, key) => { - if (!value) { return; } + public processOptions(options: Options) { + this.processObject(options); + } + + private processObject(objectToProcess: object) { + _.forEach(objectToProcess, (value, key) => { + if (!value) { + return; + } - this.processComponent(key, value, options); - this.processColor(key, value, options); - this.processImage(key, value, options); + this.processComponent(key, value, objectToProcess); + this.processColor(key, value, objectToProcess); + this.processImage(key, value, objectToProcess); this.processButtonsPassProps(key, value); if (!_.isEqual(key, 'passProps') && (_.isObject(value) || _.isArray(value))) { - this.processOptions(value); + this.processObject(value); } }); } private processColor(key: string, value: any, options: Record) { if (_.isEqual(key, 'color') || _.endsWith(key, 'Color')) { - options[key] = processColor(value); + options[key] = this.colorService.toNativeColor(value); } } private processImage(key: string, value: any, options: Record) { - if (_.isEqual(key, 'icon') || _.isEqual(key, 'image') || _.endsWith(key, 'Icon') || _.endsWith(key, 'Image')) { - options[key] = resolveAssetSource(value); + if ( + _.isEqual(key, 'icon') || + _.isEqual(key, 'image') || + _.endsWith(key, 'Icon') || + _.endsWith(key, 'Image') + ) { + options[key] = this.assetService.resolveFromRequire(value); } } @@ -52,7 +69,7 @@ export class OptionsProcessor { if (value.passProps) { this.store.setPropsForId(value.componentId, value.passProps); } - options[key] = _.omit(value, 'passProps'); + options[key].passProps = undefined; } } }