Skip to content

Commit

Permalink
Road to noImplicitAny part 2 (#4424)
Browse files Browse the repository at this point in the history
Remove some implicit anys and refactor tests

* Remove tests that test that lodash cloneDeep works
  • Loading branch information
henrikra authored and guyca committed Dec 16, 2018
1 parent 9d36521 commit c27fa5c
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 222 deletions.
1 change: 0 additions & 1 deletion lib/src/adapters/NativeCommandsSender.mock.ts

This file was deleted.

213 changes: 83 additions & 130 deletions lib/src/commands/Commands.test.ts

Large diffs are not rendered by default.

75 changes: 37 additions & 38 deletions lib/src/commands/LayoutTreeCrawler.test.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,50 @@
import * as React from 'react';

import { LayoutType } from './LayoutType';
import { LayoutTreeCrawler, LayoutNode } from './LayoutTreeCrawler';
import { UniqueIdProvider } from '../adapters/UniqueIdProvider.mock';
import { Store } from '../components/Store';

describe('LayoutTreeCrawler', () => {
let uut;
let store;
let uut: LayoutTreeCrawler;
let store: Store;

beforeEach(() => {
store = new Store();
uut = new LayoutTreeCrawler(new UniqueIdProvider(), store);
});

it('crawls a layout tree and adds unique id to each node', () => {
const node: any = { type: LayoutType.Stack, children: [{ type: LayoutType.BottomTabs }] };
const node = { type: LayoutType.Stack, id: 'Stack+UNIQUE_ID', children: [{ id: 'BottomTabs+UNIQUE_ID', type: LayoutType.BottomTabs, data: {}, children: [] }], data: {} };
uut.crawl(node);
expect(node.id).toEqual('Stack+UNIQUE_ID');
expect(node.children[0].id).toEqual('BottomTabs+UNIQUE_ID');
});

it('does not generate unique id when already provided', () => {
const node = { id: 'user defined id', type: LayoutType.Stack };
const node = { id: 'user defined id', type: LayoutType.Stack, data: {}, children: [] };
uut.crawl(node);
expect(node.id).toEqual('user defined id');
});

it('crawls a layout tree and ensures data exists', () => {
const node: any = { type: LayoutType.Stack, children: [{ type: LayoutType.BottomTabs }] };
const node = { type: LayoutType.Stack, children: [{ type: LayoutType.BottomTabs, data: {}, children: [] }], data: {} };
uut.crawl(node);
expect(node.data).toEqual({});
expect(node.children[0].data).toEqual({});
});

it('crawls a layout tree and ensures children exists', () => {
const node: any = { type: LayoutType.Stack, children: [{ type: LayoutType.BottomTabs }] };
const node = { type: LayoutType.Stack, children: [{ type: LayoutType.BottomTabs, data: {}, children: [] }], data: {} };
uut.crawl(node);
expect(node.children[0].children).toEqual([]);
});

it('crawls a layout tree and asserts known layout type', () => {
const node = { type: LayoutType.Stack, children: [{ type: 'Bob' }] };
expect(() => uut.crawl(node)).toThrowError('Unknown layout type Bob');
});

it('saves passProps into store for Component nodes', () => {
const node = {
type: LayoutType.BottomTabs, children: [
{ type: LayoutType.Component, data: { name: 'the name', passProps: { myProp: 123 } } }]
type: LayoutType.BottomTabs,
children: [{ type: LayoutType.Component, data: { name: 'the name', passProps: { myProp: 123 } }, children: [] }],
data: {}
};
expect(store.getPropsForId('Component+UNIQUE_ID')).toEqual({});
uut.crawl(node);
Expand All @@ -55,31 +53,31 @@ describe('LayoutTreeCrawler', () => {

it('Components: injects options from original component class static property', () => {
const theStyle = {};
const MyComponent = class {
const MyComponent = class CoolComponent extends React.Component {
static get options() {
return theStyle;
}
};

const node: any = { type: LayoutType.Component, data: { name: 'theComponentName' } };
const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
store.setComponentClassForName('theComponentName', () => MyComponent);
uut.crawl(node);
expect(node.data.options).toEqual(theStyle);
});

it('Components: crawl does not cache options', () => {
const optionsWithTitle = (title) => {
const optionsWithTitle = (title?: string) => {
return {
topBar: {
title: {
text: title
}
}
}
};
};

const MyComponent = class {
static options(props) {
const MyComponent = class CoolComponent extends React.Component {
static options(props: {title: string}) {
return {
topBar: {
title: {
Expand All @@ -90,37 +88,37 @@ describe('LayoutTreeCrawler', () => {
}
};

const node: any = { type: LayoutType.Component, data: { name: 'theComponentName', passProps: { title: 'title' } } };
const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {}, passProps: { title: 'title' } }, children: [] };
store.setComponentClassForName('theComponentName', () => MyComponent);
uut.crawl(node);
expect(node.data.options).toEqual(optionsWithTitle('title'));

const node2: any = { type: LayoutType.Component, data: { name: 'theComponentName' } };
const node2 = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
uut.crawl(node2);
expect(node2.data.options).toEqual(optionsWithTitle(undefined));
});

it('Components: passes passProps to the static options function to be used by the user', () => {
const MyComponent = class {
static options(passProps) {
const MyComponent = class CoolComponent extends React.Component {
static options(passProps: {bar: {baz: {value: string}}}) {
return { foo: passProps.bar.baz.value };
}
};

const node: any = { type: LayoutType.Component, data: { name: 'theComponentName', passProps: { bar: { baz: { value: 'hello' } } } } };
const node = { type: LayoutType.Component, data: { name: 'theComponentName', passProps: { bar: { baz: { value: 'hello' } } }, options: {} }, children: [] };
store.setComponentClassForName('theComponentName', () => MyComponent);
uut.crawl(node);
expect(node.data.options).toEqual({ foo: 'hello' });
});

it('Components: passProps in the static options is optional', () => {
const MyComponent = class {
static options(passProps) {
const MyComponent = class CoolComponent extends React.Component {
static options(passProps: string) {
return { foo: passProps };
}
};

const node: any = { type: LayoutType.Component, data: { name: 'theComponentName' } };
const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
store.setComponentClassForName('theComponentName', () => MyComponent);
uut.crawl(node);
expect(node.data.options).toEqual({ foo: {} });
Expand All @@ -134,7 +132,7 @@ describe('LayoutTreeCrawler', () => {
},
opt: 'exists only in static'
};
const MyComponent = class {
const MyComponent = class CoolComponent extends React.Component {
static get options() {
return theStyle;
}
Expand All @@ -148,7 +146,7 @@ describe('LayoutTreeCrawler', () => {
}
};

const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: passedOptions } };
const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: passedOptions }, children: [] };
store.setComponentClassForName('theComponentName', () => MyComponent);

uut.crawl(node);
Expand All @@ -165,27 +163,27 @@ describe('LayoutTreeCrawler', () => {

it('Component: deepClones options', () => {
const theStyle = {};
const MyComponent = class {
const MyComponent = class CoolComponent extends React.Component {
static get options() {
return theStyle;
}
};

const node: any = { type: LayoutType.Component, data: { name: 'theComponentName' } };
const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
store.setComponentClassForName('theComponentName', () => MyComponent);
uut.crawl(node);
expect(node.data.options).not.toBe(theStyle);
});

it('Components: must contain data name', () => {
const node = { type: LayoutType.Component, data: {} };
const node = { type: LayoutType.Component, data: {}, children: [] };
expect(() => uut.crawl(node)).toThrowError('Missing component data.name');
});

it('Components: options default obj', () => {
const MyComponent = class { };
const MyComponent = class extends React.Component { };

const node: any = { type: LayoutType.Component, data: { name: 'theComponentName' } };
const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
store.setComponentClassForName('theComponentName', () => MyComponent);
uut.crawl(node);
expect(node.data.options).toEqual({});
Expand All @@ -197,19 +195,20 @@ describe('LayoutTreeCrawler', () => {
data: {
name: 'compName',
passProps: {}
}
},
children: []
};
uut.crawl(node);
expect(node.data.passProps).toBeUndefined();
});

describe('navigation options', () => {
let options;
let node;
let options: Record<string, any>;
let node: LayoutNode;

beforeEach(() => {
options = {};
node = { type: LayoutType.Component, data: { name: 'theComponentName', options } };
node = { type: LayoutType.Component, data: { name: 'theComponentName', options }, children: [] };
});

it('processes colors into numeric AARRGGBB', () => {
Expand Down
9 changes: 0 additions & 9 deletions lib/src/commands/LayoutTreeCrawler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ export class LayoutTreeCrawler {
}

crawl(node: LayoutNode): void {
this._assertKnownLayoutType(node.type);
node.id = node.id || this.uniqueIdProvider.generate(node.type);
node.data = node.data || {};
node.children = node.children || [];
if (node.type === LayoutType.Component) {
this._handleComponent(node);
}
Expand Down Expand Up @@ -58,12 +55,6 @@ export class LayoutTreeCrawler {
node.data.options = _.merge({}, staticOptions, passedOptions);
}

_assertKnownLayoutType(type) {
if (!LayoutType[type]) {
throw new Error(`Unknown layout type ${type}`);
}
}

_assertComponentDataName(component) {
if (!component.data.name) {
throw new Error('Missing component data.name');
Expand Down
2 changes: 1 addition & 1 deletion lib/src/commands/LayoutTreeParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { LayoutTreeParser } from './LayoutTreeParser';
import { LayoutType } from './LayoutType';

describe('LayoutTreeParser', () => {
let uut;
let uut: LayoutTreeParser;

beforeEach(() => {
uut = new LayoutTreeParser();
Expand Down
1 change: 0 additions & 1 deletion lib/src/commands/LayoutType.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ describe('LayoutType', () => {

const name = 'Stack';
expect(LayoutType[name]).toEqual(LayoutType.Stack);
expect(LayoutType['asdasd']).toEqual(undefined); //tslint:disable-line
});
});
2 changes: 1 addition & 1 deletion lib/src/commands/OptionsProcessor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as _ from 'lodash';

describe('navigation options', () => {
let uut: OptionsProcessor;
let options;
let options: Record<string, any>;
let store: Store;
beforeEach(() => {
options = {};
Expand Down
15 changes: 9 additions & 6 deletions lib/src/commands/OptionsProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ 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';

export class OptionsProcessor {
constructor(public store, public uniqueIdProvider) { }
constructor(public store: Store, public uniqueIdProvider: UniqueIdProvider) { }

public processOptions(options) {
public processOptions(options: Record<string, any>) {
_.forEach(options, (value, key) => {
if (!value) { return; }

Expand All @@ -20,19 +23,19 @@ export class OptionsProcessor {
});
}

private processColor(key, value, options) {
private processColor(key: string, value: any, options: Record<string, any>) {
if (_.isEqual(key, 'color') || _.endsWith(key, 'Color')) {
options[key] = processColor(value);
}
}

private processImage(key, value, options) {
private processImage(key: string, value: any, options: Record<string, any>) {
if (_.isEqual(key, 'icon') || _.isEqual(key, 'image') || _.endsWith(key, 'Icon') || _.endsWith(key, 'Image')) {
options[key] = resolveAssetSource(value);
}
}

private processButtonsPassProps(key, value) {
private processButtonsPassProps(key: string, value: any) {
if (_.endsWith(key, 'Buttons')) {
_.forEach(value, (button) => {
if (button.passProps && button.id) {
Expand All @@ -43,7 +46,7 @@ export class OptionsProcessor {
}
}

private processComponent(key, value, options) {
private processComponent(key: string, value: any, options: Record<string, any>) {
if (_.isEqual(key, 'component')) {
value.componentId = value.id ? value.id : this.uniqueIdProvider.generate('CustomComponent');
if (value.passProps) {
Expand Down
17 changes: 10 additions & 7 deletions lib/src/components/ComponentWrapper.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { ComponentEventsObserver } from '../events/ComponentEventsObserver';

describe('ComponentWrapper', () => {
const componentName = 'example.MyComponent';
let store;
let myComponentProps;
let store: Store;
let myComponentProps: any;
let mockedComponentEventsObserver: ComponentEventsObserver;
let componentEventsObserver: ComponentEventsObserver;
let uut: ComponentWrapper;
Expand All @@ -29,9 +29,9 @@ describe('ComponentWrapper', () => {
}

class TestParent extends React.Component<any, any> {
private ChildClass;
private ChildClass: any;

constructor(props) {
constructor(props: any) {
super(props);
this.ChildClass = props.ChildClass;
this.state = { propsFromState: {} };
Expand All @@ -57,7 +57,7 @@ describe('ComponentWrapper', () => {
it('must have componentId as prop', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
const orig = console.error;
console.error = (a) => a;
console.error = (a: any) => a;
expect(() => {
renderer.create(<NavigationComponent />);
}).toThrowError('Component example.MyComponent does not have a componentId!');
Expand Down Expand Up @@ -168,14 +168,17 @@ describe('ComponentWrapper', () => {
);
}
}
function mapStateToProps(state) {
interface RootState {
txt: string;
}
function mapStateToProps(state: RootState) {
return {
txt: state.txt
};
}
const ConnectedComp = require('react-redux').connect(mapStateToProps)(MyReduxComp);
const ReduxProvider = require('react-redux').Provider;
const initialState = { txt: 'it just works' };
const initialState: RootState = { txt: 'it just works' };
const reduxStore = require('redux').createStore((state = initialState) => state);

it(`wraps the component with a react-redux provider with passed store`, () => {
Expand Down
Loading

0 comments on commit c27fa5c

Please sign in to comment.