use polymorphic this, make methods that change wrapper contents pick up new generic props if possible and drop them if not

This commit is contained in:
jwbay
2016-03-24 00:04:16 -04:00
parent a44529fcb1
commit 554aa30876
2 changed files with 155 additions and 65 deletions
+54 -35
View File
@@ -3,13 +3,18 @@
import { shallow, mount, render, describeWithDOM, spyLifecycle } from "enzyme";
import * as React from "react";
import {Component, ReactElement} from "react";
import {Component, ReactElement, HTMLAttributes} from "react";
import {ShallowWrapper, ReactWrapper, CheerioWrapper} from "enzyme";
// Help classes/interfaces
interface MyComponentProps {
propsProperty: any;
numberProp?: number;
}
interface StatelessProps {
stateless: any;
}
interface MyComponentState {
@@ -21,6 +26,8 @@ class MyComponent extends Component<MyComponentProps, MyComponentState> {
}
}
const MyStatelessComponent = (props: StatelessProps) => <span />;
// API
namespace SpyLifecycleTest {
spyLifecycle(MyComponent);
@@ -34,11 +41,14 @@ namespace ShallowWrapperTest {
var reactElement: ReactElement<any>,
objectVal: Object,
boolVal: Boolean,
stringVal: String;
stringVal: String,
elementWrapper: ShallowWrapper<HTMLAttributes, {}>
function test_find() {
shallowWrapper = shallowWrapper.find('.selector');
elementWrapper = shallowWrapper.find('.selector');
shallowWrapper = shallowWrapper.find(MyComponent);
shallowWrapper.find(MyStatelessComponent).props().stateless;
shallowWrapper.find(MyStatelessComponent).shallow();
}
function test_findWhere() {
@@ -47,13 +57,16 @@ namespace ShallowWrapperTest {
}
function test_filter() {
shallowWrapper = shallowWrapper.filter('.selector');
shallowWrapper = shallowWrapper.filter(MyComponent);
elementWrapper = shallowWrapper.filter('.selector');
shallowWrapper = shallowWrapper.filter(MyComponent).shallow();
}
function test_filterWhere() {
shallowWrapper =
shallowWrapper.filterWhere((aShallowWrapper: ShallowWrapper<MyComponentProps, MyComponentState>) => true);
shallowWrapper.filterWhere(wrapper => {
wrapper.props().propsProperty;
return true;
});
}
function test_contains() {
@@ -69,11 +82,12 @@ namespace ShallowWrapperTest {
}
function test_not() {
shallowWrapper = shallowWrapper.find('.foo').not('.bar');
elementWrapper = shallowWrapper.find('.foo').not('.bar');
}
function test_children() {
shallowWrapper = shallowWrapper.children();
shallowWrapper.children(MyStatelessComponent).props().stateless;
}
function test_parents() {
@@ -85,7 +99,7 @@ namespace ShallowWrapperTest {
}
function test_closest() {
shallowWrapper = shallowWrapper.closest('.selector');
elementWrapper = shallowWrapper.closest('.selector');
shallowWrapper = shallowWrapper.closest(MyComponent);
}
@@ -171,20 +185,18 @@ namespace ShallowWrapperTest {
function test_forEach() {
shallowWrapper =
shallowWrapper.forEach((aShallowWrapper: ShallowWrapper<MyComponentProps, MyComponentState>)=> {
});
shallowWrapper.forEach(wrapper => wrapper.shallow().props().propsProperty);
}
function test_map() {
var arrayVal: Array<any> =
shallowWrapper.map((aShallowWrapper: ShallowWrapper<MyComponentProps, MyComponentState>)=> {
});
var arrayNumbers: Array<Number> =
shallowWrapper.map(wrapper => wrapper.props().numberProp);
}
function test_reduce() {
const total: number[] =
shallowWrapper.reduce<number>(
(amount: number, n: ShallowWrapper<MyComponentProps, MyComponentState>) => amount + n.prop('amount')
shallowWrapper.reduce(
(amount: number, n: ShallowWrapper<MyComponentProps, MyComponentState>) => amount + n.props().numberProp
);
}
@@ -223,11 +235,13 @@ namespace ReactWrapperTest {
var reactElement: ReactElement<any>,
objectVal: Object,
boolVal: Boolean,
stringVal: String;
stringVal: String,
elementWrapper: ReactWrapper<HTMLAttributes, {}>
function test_find() {
reactWrapper = reactWrapper.find('.selector');
elementWrapper = reactWrapper.find('.selector');
reactWrapper = reactWrapper.find(MyComponent);
reactWrapper.find(MyStatelessComponent).props().stateless;
}
function test_findWhere() {
@@ -236,13 +250,16 @@ namespace ReactWrapperTest {
}
function test_filter() {
reactWrapper = reactWrapper.filter('.selector');
elementWrapper = reactWrapper.filter('.selector');
reactWrapper = reactWrapper.filter(MyComponent);
}
function test_filterWhere() {
reactWrapper =
reactWrapper.filterWhere((aReactWrapper: ReactWrapper<MyComponentProps, MyComponentState>) => true);
reactWrapper.filterWhere(wrapper => {
wrapper.props().propsProperty;
return true;
});
}
function test_contains() {
@@ -258,7 +275,7 @@ namespace ReactWrapperTest {
}
function test_not() {
reactWrapper = reactWrapper.find('.foo').not('.bar');
elementWrapper = reactWrapper.find('.foo').not('.bar');
}
function test_children() {
@@ -274,7 +291,7 @@ namespace ReactWrapperTest {
}
function test_closest() {
reactWrapper = reactWrapper.closest('.selector');
elementWrapper = reactWrapper.closest('.selector');
reactWrapper = reactWrapper.closest(MyComponent);
}
@@ -351,14 +368,12 @@ namespace ReactWrapperTest {
function test_forEach() {
reactWrapper =
reactWrapper.forEach((aReactWrapper: ReactWrapper<MyComponentProps, MyComponentState>)=> {
});
reactWrapper.forEach(wrapper => wrapper.props().propsProperty);
}
function test_map() {
var arrayVal: Array<any> =
reactWrapper.map((aReactWrapper: ReactWrapper<MyComponentProps, MyComponentState>)=> {
});
var arrayNumbers: Array<Number> =
reactWrapper.map(wrapper => wrapper.props().numberProp);
}
function test_reduce() {
@@ -402,11 +417,13 @@ namespace CheerioWrapperTest {
var reactElement: ReactElement<any>,
objectVal: Object,
boolVal: Boolean,
stringVal: String;
stringVal: String,
elementWrapper: ReactWrapper<HTMLAttributes, {}>
function test_find() {
cheerioWrapper = cheerioWrapper.find('.selector');
elementWrapper = cheerioWrapper.find('.selector');
cheerioWrapper = cheerioWrapper.find(MyComponent);
cheerioWrapper.find(MyStatelessComponent).props().stateless;
}
function test_findWhere() {
@@ -415,13 +432,16 @@ namespace CheerioWrapperTest {
}
function test_filter() {
cheerioWrapper = cheerioWrapper.filter('.selector');
elementWrapper = cheerioWrapper.filter('.selector');
cheerioWrapper = cheerioWrapper.filter(MyComponent);
}
function test_filterWhere() {
cheerioWrapper =
cheerioWrapper.filterWhere((aCheerioWrapper: CheerioWrapper<MyComponentProps, MyComponentState>) => true);
cheerioWrapper.filterWhere(wrapper => {
wrapper.props().propsProperty;
return true;
});
}
function test_contains() {
@@ -437,7 +457,7 @@ namespace CheerioWrapperTest {
}
function test_not() {
cheerioWrapper = cheerioWrapper.find('.foo').not('.bar');
elementWrapper = cheerioWrapper.find('.foo').not('.bar');
}
function test_children() {
@@ -453,7 +473,7 @@ namespace CheerioWrapperTest {
}
function test_closest() {
cheerioWrapper = cheerioWrapper.closest('.selector');
elementWrapper = cheerioWrapper.closest('.selector');
cheerioWrapper = cheerioWrapper.closest(MyComponent);
}
@@ -535,9 +555,8 @@ namespace CheerioWrapperTest {
}
function test_map() {
var arrayVal: Array<any> =
cheerioWrapper.map((aCheerioWrapper: CheerioWrapper<MyComponentProps, MyComponentState>)=> {
});
var arrayNumbers: Array<Number> =
cheerioWrapper.map(wrapper => wrapper.props().numberProp);
}
function test_reduce() {
+101 -30
View File
@@ -7,7 +7,7 @@
declare module "enzyme" {
import {ReactElement, Component} from "react";
import {ReactElement, Component, StatelessComponent, ComponentClass, HTMLAttributes} from "react";
export class ElementClass extends Component<any, any> {
}
@@ -22,30 +22,34 @@ declare module "enzyme" {
*/
export type EnzymeSelector = String | typeof ElementClass;
interface CommonWrapper<T, P, S> {
interface CommonWrapper<P, S> {
/**
* Find every node in the render tree that matches the provided selector.
* @param selector The selector to match.
*/
find(selector: EnzymeSelector): T;
find<P2>(component: ComponentClass<P2>): CommonWrapper<P2, any>;
find<P2>(statelessComponent: StatelessComponent<P2>): CommonWrapper<P2, {}>;
find(selector: string): CommonWrapper<HTMLAttributes, any>;
/**
* Finds every node in the render tree that returns true for the provided predicate function.
* @param predicate
*/
findWhere(predicate: (shallowWrapper: ShallowWrapper<P, S>) => Boolean): T;
findWhere(predicate: (wrapper: CommonWrapper<any, any>) => Boolean): CommonWrapper<any, any>;
/**
* Removes nodes in the current wrapper that do not match the provided selector.
* @param selector The selector to match.
*/
filter(selector: EnzymeSelector): T;
filter<P2>(component: ComponentClass<P2>): CommonWrapper<P2, any>;
filter<P2>(statelessComponent: StatelessComponent<P2>): CommonWrapper<P2, {}>;
filter(selector: string): CommonWrapper<HTMLAttributes, any>;
/**
* Returns a new wrapper with only the nodes of the current wrapper that, when passed into the provided predicate function, return true.
* @param predicate
*/
filterWhere(predicate: (shallowWrapper: ShallowWrapper<P, S>) => Boolean): T;
filterWhere(predicate: (wrapper: this) => Boolean): this;
/**
* Returns whether or not the current wrapper has a node anywhere in it's render tree that looks like the one passed in.
@@ -70,14 +74,17 @@ declare module "enzyme" {
* This method is effectively the negation or inverse of filter.
* @param selector
*/
not(selector: EnzymeSelector): T;
not(selector: EnzymeSelector): this;
/**
* Returns a new wrapper with all of the children of the node(s) in the current wrapper. Optionally, a selector
* can be provided and it will filter the children by this selector.
* @param [selector]
*/
children(selector?: EnzymeSelector): T;
children<P2>(component: ComponentClass<P2>): CommonWrapper<P2, any>;
children<P2>(statelessComponent: StatelessComponent<P2>): CommonWrapper<P2, {}>;
children(selector: string): CommonWrapper<HTMLAttributes, any>;
children(): CommonWrapper<any, any>;
/**
* Returns a wrapper around all of the parents/ancestors of the wrapper. Does not include the node in the
@@ -86,12 +93,15 @@ declare module "enzyme" {
* Note: can only be called on a wrapper of a single node.
* @param [selector]
*/
parents(selector?: EnzymeSelector): T;
parents<P2>(component: ComponentClass<P2>): CommonWrapper<P2, any>;
parents<P2>(statelessComponent: StatelessComponent<P2>): CommonWrapper<P2, {}>;
parents(selector: string): CommonWrapper<HTMLAttributes, any>;
parents(): CommonWrapper<any, any>;
/**
* Returns a wrapper with the direct parent of the node in the current wrapper.
*/
parent(): T;
parent(): CommonWrapper<any, any>;
/**
* Returns a wrapper of the first element that matches the selector by traversing up through the current node's
@@ -100,7 +110,9 @@ declare module "enzyme" {
* Note: can only be called on a wrapper of a single node.
* @param selector
*/
closest(selector: EnzymeSelector): T;
closest<P2>(component: ComponentClass<P2>): CommonWrapper<P2, any>;
closest<P2>(statelessComponent: StatelessComponent<P2>): CommonWrapper<P2, {}>;
closest(selector: string): CommonWrapper<HTMLAttributes, any>;
/**
* Returns a string of the rendered text of the current render tree. This function should be looked at with
@@ -128,17 +140,17 @@ declare module "enzyme" {
* Returns a wrapper around the node at a given index of the current wrapper.
* @param index
*/
at(index: number): T;
at(index: number): this;
/**
* Reduce the set of matched nodes to the first in the set.
*/
first(): T;
first(): this;
/**
* Reduce the set of matched nodes to the last in the set.
*/
last(): T;
last(): this;
/**
* Returns the state hash for the root node of the wrapper. Optionally pass in a prop name and it will return just that value.
@@ -151,7 +163,7 @@ declare module "enzyme" {
*
* NOTE: can only be called on a wrapper of a single node.
*/
props(): Object;
props(): P;
/**
* Returns the prop value for the node of the current wrapper with the provided key.
@@ -167,7 +179,7 @@ declare module "enzyme" {
* @param event
* @param args?
*/
simulate(event: String, ...args: any[]): T;
simulate(event: string, ...args: any[]): this;
/**
* A method to invoke setState() on the root component instance similar to how you might in the definition of
@@ -180,7 +192,7 @@ declare module "enzyme" {
* NOTE: can only be called on a wrapper instance that is also the root instance.
* @param state
*/
setState(state: S): T;
setState(state: S): this;
/**
* A method that sets the props of the root component, and re-renders. Useful for when you are wanting to test
@@ -193,7 +205,7 @@ declare module "enzyme" {
* NOTE: can only be called on a wrapper instance that is also the root instance.
* @param state
*/
setProps(state: Object): T;
setProps(props: P): this;
/**
* A method that sets the context of the root component, and re-renders. Useful for when you are wanting to
@@ -203,7 +215,7 @@ declare module "enzyme" {
* NOTE: can only be called on a wrapper instance that is also the root instance.
* @param state
*/
setContext(state: Object): T;
setContext(context: Object): this;
/**
* Gets the instance of the component being rendered as the root node passed into shallow().
@@ -219,7 +231,7 @@ declare module "enzyme" {
*
* NOTE: can only be called on a wrapper instance that is also the root instance.
*/
update(): T;
update(): this;
/**
* Returns an html-like string of the wrapper for debugging purposes. Useful to print out to the console when
@@ -243,7 +255,7 @@ declare module "enzyme" {
* @param fn A callback to be run for every node in the collection. Should expect a ShallowWrapper as the first
* argument, and will be run with a context of the original instance.
*/
forEach(fn: (wrapper: ShallowWrapper<P, S>) => void): T;
forEach(fn: (wrapper: this) => any): this;
/**
* Maps the current array of nodes to another array. Each node is passed in as a ShallowWrapper to the map
@@ -253,7 +265,7 @@ declare module "enzyme" {
* to the returned array. Should expect a ShallowWrapper as the first argument, and will be run
* with a context of the original instance.
*/
map(fn: (wrapper: ShallowWrapper<P, S>) => any): Array<any>;
map<V>(fn: (wrapper: this) => V): V[];
/**
* Applies the provided reducing function to every node in the wrapper to reduce to a single value. Each node
@@ -261,7 +273,7 @@ declare module "enzyme" {
* @param fn
* @param initialValue
*/
reduce<R>(fn: (prevVal: R, wrapper: ShallowWrapper<P, S>, index: number) => R, initialValue?: R): R[];
reduce<R>(fn: (prevVal: R, wrapper: this, index: number) => R, initialValue?: R): R[];
/**
* Applies the provided reducing function to every node in the wrapper to reduce to a single value.
@@ -269,7 +281,7 @@ declare module "enzyme" {
* @param fn
* @param initialValue
*/
reduceRight<R>(fn: (prevVal: R, wrapper: ShallowWrapper<P, S>, index: number) => R, initialValue?: R): R[];
reduceRight<R>(fn: (prevVal: R, wrapper: this, index: number) => R, initialValue?: R): R[];
/**
* Returns whether or not any of the nodes in the wrapper match the provided selector.
@@ -281,7 +293,7 @@ declare module "enzyme" {
* Returns whether or not any of the nodes in the wrapper pass the provided predicate function.
* @param fn
*/
someWhere(fn: (wrapper: ShallowWrapper<P, S>) => Boolean): Boolean;
someWhere(fn: (wrapper: this) => Boolean): Boolean;
/**
* Returns whether or not all of the nodes in the wrapper match the provided selector.
@@ -293,22 +305,81 @@ declare module "enzyme" {
* Returns whether or not any of the nodes in the wrapper pass the provided predicate function.
* @param fn
*/
everyWhere(fn: (wrapper: ShallowWrapper<P, S>) => Boolean): Boolean;
everyWhere(fn: (wrapper: this) => Boolean): Boolean;
length: number;
}
export interface ShallowWrapper<P, S> extends CommonWrapper<ShallowWrapper<P, S>, P, S> {
export interface ShallowWrapper<P, S> extends CommonWrapper<P, S> {
shallow(): ShallowWrapper<P, S>;
render(): CheerioWrapper<P, S>;
/**
* Find every node in the render tree that matches the provided selector.
* @param selector The selector to match.
*/
find<P2>(component: ComponentClass<P2>): ShallowWrapper<P2, any>;
find<P2>(statelessComponent: (props: P2) => JSX.Element): ShallowWrapper<P2, {}>;
find(selector: string): ShallowWrapper<HTMLAttributes, any>;
/**
* Removes nodes in the current wrapper that do not match the provided selector.
* @param selector The selector to match.
*/
filter<P2>(component: ComponentClass<P2>): ShallowWrapper<P2, any>;
filter<P2>(statelessComponent: StatelessComponent<P2>): ShallowWrapper<P2, {}>;
filter(selector: string): ShallowWrapper<HTMLAttributes, any>;
/**
* Finds every node in the render tree that returns true for the provided predicate function.
* @param predicate
*/
findWhere(predicate: (wrapper: CommonWrapper<any, any>) => Boolean): ShallowWrapper<any, any>;
/**
* Returns a new wrapper with all of the children of the node(s) in the current wrapper. Optionally, a selector
* can be provided and it will filter the children by this selector.
* @param [selector]
*/
children<P2>(component: ComponentClass<P2>): ShallowWrapper<P2, any>;
children<P2>(statelessComponent: StatelessComponent<P2>): ShallowWrapper<P2, {}>;
children(selector: string): ShallowWrapper<HTMLAttributes, any>;
children(): ShallowWrapper<any, any>;
/**
* Returns a wrapper around all of the parents/ancestors of the wrapper. Does not include the node in the
* current wrapper. Optionally, a selector can be provided and it will filter the parents by this selector.
*
* Note: can only be called on a wrapper of a single node.
* @param [selector]
*/
parents<P2>(component: ComponentClass<P2>): ShallowWrapper<P2, any>;
parents<P2>(statelessComponent: StatelessComponent<P2>): ShallowWrapper<P2, {}>;
parents(selector: string): ShallowWrapper<HTMLAttributes, any>;
parents(): ShallowWrapper<any, any>;
/**
* Returns a wrapper of the first element that matches the selector by traversing up through the current node's
* ancestors in the tree, starting with itself.
*
* Note: can only be called on a wrapper of a single node.
* @param selector
*/
closest<P2>(component: ComponentClass<P2>): ShallowWrapper<P2, any>;
closest<P2>(statelessComponent: StatelessComponent<P2>): ShallowWrapper<P2, {}>;
closest(selector: string): ShallowWrapper<HTMLAttributes, any>;
/**
* Returns a wrapper with the direct parent of the node in the current wrapper.
*/
parent(): ShallowWrapper<any, any>;
}
export interface ReactWrapper<P, S> extends CommonWrapper<ReactWrapper<P, S>, P, S> {
export interface ReactWrapper<P, S> extends CommonWrapper<P, S> {
}
export interface CheerioWrapper<P, S> extends CommonWrapper<CheerioWrapper<P, S>, P, S> {
export interface CheerioWrapper<P, S> extends CommonWrapper<P, S> {
}