Skip to content

Commit 581276a

Browse files
added support for nthNode (#127)
1 parent fc137d8 commit 581276a

File tree

6 files changed

+100
-34
lines changed

6 files changed

+100
-34
lines changed

README.md

+27
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Internally, cypress-react-selector uses a library called [resq](https://github.c
2727
- [Get React Properties from element](#get-react-properties-from-element)
2828
- [Get Props](#get-props)
2929
- [Get current state](#get-current-state)
30+
- [Timeouts](#timeouts)
31+
- [Fetch indexed node](#fetch-indexed-node)
3032
- [Use fluent chained queries](#use-fluent-chained-queries)
3133
- [Sample Tests](#sample-tests)
3234
- [Community Projects](#community-projects)
@@ -258,6 +260,31 @@ cy.getReact('MyTextInput', {
258260
}).getCurrentState(); // can return string | boolean | any[] | {}
259261
```
260262

263+
## Timeouts
264+
265+
You can configure the [timeouts](https://docs.cypress.io/guides/references/configuration.html#Timeouts) in the `cypress.json` configuration file. Alternatively, you can also pass the `timeout` as a object literal in the react commands like,
266+
267+
```js
268+
cy.react('MyComponent', { options: { timeout: 50000 } });
269+
```
270+
271+
## Fetch indexed node
272+
273+
1. `cy.react` returns DOM element, so you can fetch the indexed node by [.eq(index)](https://docs.cypress.io/api/commands/eq.html), like:
274+
275+
```js
276+
cy.react('MyComponent').eq(0).click();
277+
```
278+
279+
2. `cy.getReact()` return RESQ node, so you can't fetch it through `.eq()`. You need to use `.nthNode(index)`, like:
280+
281+
```js
282+
cy.getReact('MyComponent')
283+
.nthNode(0)
284+
.getProps('name')
285+
.should('eq', 'First Item');
286+
```
287+
261288
## Use fluent chained queries
262289

263290
:warning: Fluent commands are not working in some special cases. It is being tracked [here](https://github.com/abhinaba-ghosh/cypress-react-selector/issues/48)

cypress/component/components/ProductsList.spec.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe('Selecting by React props and state', () => {
5252

5353
it('find React components', () => {
5454
cy.log('**cy.getReact**');
55-
// returns React component wrapper with props
55+
//returns React component wrapper with props
5656
cy.getReact('AProduct', { props: { name: 'Second item' } })
5757
.getProps()
5858
.should('deep.include', { name: 'Second item' });
@@ -69,6 +69,12 @@ describe('Selecting by React props and state', () => {
6969
cy.getReact('AProduct', { state: { myName: 'Second item' } }).should(
7070
'exist'
7171
);
72+
73+
//find component using index
74+
cy.getReact('AProduct')
75+
.nthNode(1)
76+
.getProps('name')
77+
.should('eq', 'Second item');
7278
});
7379

7480
it('chains getReact', () => {

index.d.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,13 @@ declare namespace Cypress {
3838
*/
3939
react<E extends Node = HTMLElement>(
4040
component: string,
41-
reactOpts?: {},
42-
options?: Partial<Loggable & Timeoutable>
41+
reactOpts?: {
42+
props?: object;
43+
state?: object;
44+
exact?: boolean;
45+
root?: string;
46+
options?: Partial<Loggable & Timeoutable>;
47+
}
4348
): Chainable<JQuery<E>>;
4449

4550
/**
@@ -54,8 +59,13 @@ declare namespace Cypress {
5459
*/
5560
getReact(
5661
component: string,
57-
reactOpts?: {},
58-
options?: Partial<Loggable & Timeoutable>
62+
reactOpts?: {
63+
props?: object;
64+
state?: object;
65+
exact?: boolean;
66+
root?: string;
67+
options?: Partial<Loggable & Timeoutable>;
68+
}
5969
): Chainable<RESQNode>;
6070

6171
/**
@@ -78,5 +88,14 @@ declare namespace Cypress {
7888
* This method should always be used with getReact() method
7989
*/
8090
getCurrentState(): Chainable<any>;
91+
92+
/**
93+
* Get the nthNode using index
94+
* @param index
95+
*
96+
* @example
97+
* cy.getReact('Product').nthNode(0).getProps('name').should('eq', 'First item');
98+
*/
99+
nthNode(index: string): Chainable<any>;
81100
}
82101
}

index.js

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const {
44
getReact,
55
getProps,
66
getCurrentState,
7+
nthNode,
78
} = require('./src/reactHandler');
89

910
// add cypress custom commands
@@ -12,3 +13,4 @@ Cypress.Commands.add('react', { prevSubject: ['optional', 'element'] }, react);
1213
Cypress.Commands.add('getReact', { prevSubject: 'optional' }, getReact);
1314
Cypress.Commands.add('getProps', { prevSubject: true }, getProps);
1415
Cypress.Commands.add('getCurrentState', { prevSubject: true }, getCurrentState);
16+
Cypress.Commands.add('nthNode', { prevSubject: true }, nthNode);

src/reactHandler.js

+18-29
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {
66
getReactRoot,
77
getDefaultCommandOptions,
88
checkReactOptsIsValid,
9+
getReactNode,
910
} = require('./utils');
1011

1112
/**
@@ -253,53 +254,41 @@ exports.getReact = (subject, component, reactOpts = {}) => {
253254
* @param {*} propName
254255
*/
255256
exports.getProps = (subject, propName) => {
256-
if (!subject || !subject[0].props) {
257-
throw new Error(
258-
'Previous subject found null. getProps() is a child command. Use with cy.getReact()'
259-
);
260-
}
261-
if (subject.length > 1) {
262-
throw new Error(
263-
`getProps() works with single React Node. React Node found ${subject.length}`
264-
);
265-
}
257+
const reactNode = getReactNode(subject);
266258
cy.log(`Finding value for prop **${propName || 'all props'}**`);
267259
cy.log(
268260
`Prop value found **${
269261
propName
270-
? safeStringify(getJsonValue(subject[0].props, propName))
271-
: safeStringify(subject[0].props)
262+
? safeStringify(getJsonValue(reactNode.props, propName))
263+
: safeStringify(reactNode.props)
272264
}**`
273265
);
274266
const propValue = propName
275-
? cy.wrap(getJsonValue(subject[0].props, propName))
276-
: cy.wrap(subject[0].props);
267+
? cy.wrap(getJsonValue(reactNode.props, propName))
268+
: cy.wrap(reactNode.props);
277269
return propValue;
278270
};
279271

280272
/**
281273
* get all props or specific props from react node
282274
* @param {*} subject
283-
* @param {*} propName
284275
*/
285276
exports.getCurrentState = (subject) => {
286-
if (!subject || !subject[0].state) {
287-
throw new Error(
288-
'Previous subject found null. getCurrentState() is a child command. Use with cy.getReact()'
289-
);
290-
}
291-
if (subject.length > 1) {
292-
throw new Error(
293-
`getCurrentState() works with single React Node. React Node found ${subject.length}`
294-
);
295-
}
277+
const reactNode = getReactNode(subject);
296278
cy.log(`Finding current state of the React component`);
297279
cy.log(
298280
`Current state found **${
299-
getType(subject[0].state) === 'object'
300-
? safeStringify(subject[0].state)
301-
: subject[0].state
281+
getType(reactNode.state) === 'object'
282+
? safeStringify(reactNode.state)
283+
: reactNode.state
302284
}**`
303285
);
304-
return cy.wrap(subject[0].state);
286+
return cy.wrap(reactNode.state);
305287
};
288+
289+
/**
290+
* Get nth Node
291+
* @param {object} subject
292+
* @param {number} pos
293+
*/
294+
exports.nthNode = (subject, pos) => cy.wrap(subject).then((e) => e[pos]);

src/utils.js

+23
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,26 @@ exports.checkReactOptsIsValid = (reactOpts) => {
8888
return true;
8989
}
9090
};
91+
92+
/**
93+
* Validate and get resq node for fetching props and states
94+
* @param {object || object[]} subject
95+
*/
96+
exports.getReactNode = (subject) => {
97+
if (!subject) {
98+
throw new Error(
99+
'Previous subject found null. getCurrentState() is a child command. Use with cy.getReact()'
100+
);
101+
}
102+
if (
103+
Array.isArray(subject) &&
104+
(subject[0].props || subject[0].state) &&
105+
subject.length > 1
106+
) {
107+
throw new Error(
108+
`getCurrentState() works with single React Node. Number of React Node found ${subject.length}. Use nthNode(index) to fetch an unique react node`
109+
);
110+
}
111+
112+
return Array.isArray(subject) ? subject[0] : subject;
113+
};

0 commit comments

Comments
 (0)