From 554aa308768307e1ea87a021783cade90f64b484 Mon Sep 17 00:00:00 2001 From: jwbay Date: Wed, 23 Mar 2016 23:27:14 -0400 Subject: [PATCH] use polymorphic `this`, make methods that change wrapper contents pick up new generic props if possible and drop them if not --- enzyme/enzyme-tests.tsx | 89 ++++++++++++++++----------- enzyme/enzyme.d.ts | 131 +++++++++++++++++++++++++++++++--------- 2 files changed, 155 insertions(+), 65 deletions(-) diff --git a/enzyme/enzyme-tests.tsx b/enzyme/enzyme-tests.tsx index 26325747c4..56767b4ab3 100644 --- a/enzyme/enzyme-tests.tsx +++ b/enzyme/enzyme-tests.tsx @@ -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 { } } +const MyStatelessComponent = (props: StatelessProps) => ; + // API namespace SpyLifecycleTest { spyLifecycle(MyComponent); @@ -34,11 +41,14 @@ namespace ShallowWrapperTest { var reactElement: ReactElement, objectVal: Object, boolVal: Boolean, - stringVal: String; + stringVal: String, + elementWrapper: ShallowWrapper 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) => 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)=> { - }); + shallowWrapper.forEach(wrapper => wrapper.shallow().props().propsProperty); } function test_map() { - var arrayVal: Array = - shallowWrapper.map((aShallowWrapper: ShallowWrapper)=> { - }); + var arrayNumbers: Array = + shallowWrapper.map(wrapper => wrapper.props().numberProp); } function test_reduce() { const total: number[] = - shallowWrapper.reduce( - (amount: number, n: ShallowWrapper) => amount + n.prop('amount') + shallowWrapper.reduce( + (amount: number, n: ShallowWrapper) => amount + n.props().numberProp ); } @@ -223,11 +235,13 @@ namespace ReactWrapperTest { var reactElement: ReactElement, objectVal: Object, boolVal: Boolean, - stringVal: String; + stringVal: String, + elementWrapper: ReactWrapper 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) => 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)=> { - }); + reactWrapper.forEach(wrapper => wrapper.props().propsProperty); } function test_map() { - var arrayVal: Array = - reactWrapper.map((aReactWrapper: ReactWrapper)=> { - }); + var arrayNumbers: Array = + reactWrapper.map(wrapper => wrapper.props().numberProp); } function test_reduce() { @@ -402,11 +417,13 @@ namespace CheerioWrapperTest { var reactElement: ReactElement, objectVal: Object, boolVal: Boolean, - stringVal: String; + stringVal: String, + elementWrapper: ReactWrapper 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) => 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 = - cheerioWrapper.map((aCheerioWrapper: CheerioWrapper)=> { - }); + var arrayNumbers: Array = + cheerioWrapper.map(wrapper => wrapper.props().numberProp); } function test_reduce() { diff --git a/enzyme/enzyme.d.ts b/enzyme/enzyme.d.ts index dd0c996a7c..9047fbf728 100644 --- a/enzyme/enzyme.d.ts +++ b/enzyme/enzyme.d.ts @@ -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 { } @@ -22,30 +22,34 @@ declare module "enzyme" { */ export type EnzymeSelector = String | typeof ElementClass; - interface CommonWrapper { + interface CommonWrapper { /** * Find every node in the render tree that matches the provided selector. * @param selector The selector to match. */ - find(selector: EnzymeSelector): T; + find(component: ComponentClass): CommonWrapper; + find(statelessComponent: StatelessComponent): CommonWrapper; + find(selector: string): CommonWrapper; /** * Finds every node in the render tree that returns true for the provided predicate function. * @param predicate */ - findWhere(predicate: (shallowWrapper: ShallowWrapper) => Boolean): T; + findWhere(predicate: (wrapper: CommonWrapper) => Boolean): CommonWrapper; /** * Removes nodes in the current wrapper that do not match the provided selector. * @param selector The selector to match. */ - filter(selector: EnzymeSelector): T; + filter(component: ComponentClass): CommonWrapper; + filter(statelessComponent: StatelessComponent): CommonWrapper; + filter(selector: string): CommonWrapper; /** * 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) => 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(component: ComponentClass): CommonWrapper; + children(statelessComponent: StatelessComponent): CommonWrapper; + children(selector: string): CommonWrapper; + children(): CommonWrapper; /** * 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(component: ComponentClass): CommonWrapper; + parents(statelessComponent: StatelessComponent): CommonWrapper; + parents(selector: string): CommonWrapper; + parents(): CommonWrapper; /** * Returns a wrapper with the direct parent of the node in the current wrapper. */ - parent(): T; + parent(): CommonWrapper; /** * 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(component: ComponentClass): CommonWrapper; + closest(statelessComponent: StatelessComponent): CommonWrapper; + closest(selector: string): CommonWrapper; /** * 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) => 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) => any): Array; + map(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(fn: (prevVal: R, wrapper: ShallowWrapper, index: number) => R, initialValue?: R): R[]; + reduce(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(fn: (prevVal: R, wrapper: ShallowWrapper, index: number) => R, initialValue?: R): R[]; + reduceRight(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) => 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) => Boolean): Boolean; + everyWhere(fn: (wrapper: this) => Boolean): Boolean; length: number; } - export interface ShallowWrapper extends CommonWrapper, P, S> { + export interface ShallowWrapper extends CommonWrapper { shallow(): ShallowWrapper; - render(): CheerioWrapper; + + /** + * Find every node in the render tree that matches the provided selector. + * @param selector The selector to match. + */ + find(component: ComponentClass): ShallowWrapper; + find(statelessComponent: (props: P2) => JSX.Element): ShallowWrapper; + find(selector: string): ShallowWrapper; + + /** + * Removes nodes in the current wrapper that do not match the provided selector. + * @param selector The selector to match. + */ + filter(component: ComponentClass): ShallowWrapper; + filter(statelessComponent: StatelessComponent): ShallowWrapper; + filter(selector: string): ShallowWrapper; + + /** + * Finds every node in the render tree that returns true for the provided predicate function. + * @param predicate + */ + findWhere(predicate: (wrapper: CommonWrapper) => Boolean): ShallowWrapper; + + /** + * 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(component: ComponentClass): ShallowWrapper; + children(statelessComponent: StatelessComponent): ShallowWrapper; + children(selector: string): ShallowWrapper; + children(): ShallowWrapper; + + /** + * 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(component: ComponentClass): ShallowWrapper; + parents(statelessComponent: StatelessComponent): ShallowWrapper; + parents(selector: string): ShallowWrapper; + parents(): ShallowWrapper; + + /** + * 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(component: ComponentClass): ShallowWrapper; + closest(statelessComponent: StatelessComponent): ShallowWrapper; + closest(selector: string): ShallowWrapper; + + /** + * Returns a wrapper with the direct parent of the node in the current wrapper. + */ + parent(): ShallowWrapper; } - export interface ReactWrapper extends CommonWrapper, P, S> { + export interface ReactWrapper extends CommonWrapper { } - export interface CheerioWrapper extends CommonWrapper, P, S> { + export interface CheerioWrapper extends CommonWrapper { }