Skip to content

Commit 267deee

Browse files
committed
docs: Add failure screenshot
* Remove need for an http server. Followed example from cypress-tab-plugin * Fix issue where selectors were no longer logged when pipe failed to find an element when traversals were used
1 parent 9e90a1c commit 267deee

File tree

8 files changed

+178
-539
lines changed

8 files changed

+178
-539
lines changed

Diff for: README.md

+18
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,24 @@ cy.wrap({ foo: 'bar' })
138138
// - ASSERT expected 'bar' to equal 'bar'
139139
```
140140

141+
Here's a screenshot of a failure using `cypress-pipe` and `loggable`:
142+
143+
![cypress-pipe failure](./failure.png)
144+
145+
The code that produced this was:
146+
147+
```ts
148+
const getFirst = $el => $el.find('#first')
149+
const getSecond = loggable('getSecond', selector => $el => $el.find(selector))
150+
151+
cy.get('body')
152+
.pipe(getFirst)
153+
.pipe(getSecond('#second1'))
154+
.should('contain', 'foobar')
155+
```
156+
157+
Pipe tries to add as much debugging information as possible.
158+
141159

142160
## Installation
143161
```

Diff for: cypress.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"baseUrl": "http://localhost:3333"
2+
33
}

Diff for: index.html renamed to cypress/fixtures/index.html

File renamed without changes.

Diff for: cypress/integration/example_spec.js

+34-9
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ describe('loggable', () => {
2222
context('when a name is provided', () => {
2323
it('should set the displayName of the function', () => {
2424
const getFoo = loggable('getFoo', () => 'foo')
25-
console.log('getFoo', getFoo)
2625
expect(getFoo.displayName).to.equal('getFoo')
2726
})
2827
})
@@ -99,7 +98,7 @@ describe('pipe()', () => {
9998
.pipe(subject => subject.foo.bar)
10099
.should('equal', 'baz')
101100
})
102-
101+
103102
it('should allow for a specified timeout', (done) => {
104103
const obj = { foo: 'bar' }
105104
setTimeout(() => { obj.foo = 'baz' }, delay)
@@ -110,7 +109,7 @@ describe('pipe()', () => {
110109
// we should not get here
111110
done(new Error('Failed to fail on timeout'))
112111
})
113-
112+
114113
cy.on('fail', (err) => {
115114
expect(err.message).to.include('Timed out retrying')
116115
expect(err.message).to.include("expected 'bar' to equal 'baz'")
@@ -124,9 +123,9 @@ describe('pipe()', () => {
124123
const getText = $el => $el.text()
125124

126125
beforeEach(() => {
127-
cy.visit('/')
126+
cy.visit('/cypress/fixtures/')
128127
})
129-
128+
130129
it('should have the correct element in the Command Log', () => {
131130
let lastLog
132131

@@ -150,7 +149,7 @@ describe('pipe()', () => {
150149
lastLog = log
151150
}
152151
})
153-
152+
154153
cy.get('body')
155154
.pipe(getFirst)
156155
.should(() => {
@@ -169,6 +168,32 @@ describe('pipe()', () => {
169168
.should('equal', 'foobar')
170169
})
171170

171+
it('should fail with the selector in the error message if element is not found', (done) => {
172+
const assertMessage = error => {
173+
expect(error.message).to.contain('#wontfind')
174+
Cypress.off('fail', assertMessage)
175+
done()
176+
}
177+
Cypress.on('fail', assertMessage)
178+
179+
const thisWillFail = $el => $el.find('#wontfind')
180+
cy.get('body')
181+
.pipe(thisWillFail, { timeout: 50 })
182+
})
183+
184+
it('should fail with the selector in the error message if element is not found even if multiple traversals are used', (done) => {
185+
const assertMessage = error => {
186+
expect(error.message).to.contain('#wontfind')
187+
Cypress.off('fail', assertMessage)
188+
done()
189+
}
190+
Cypress.on('fail', assertMessage)
191+
192+
const thisWillFail = $el => $el.find('#first').find('#wontfind')
193+
cy.get('body')
194+
.pipe(thisWillFail, { timeout: 50 })
195+
})
196+
172197
it('should create a snapshot', () => {
173198
let lastLog
174199
cy.on('log:added', (attrs, log) => {
@@ -223,7 +248,7 @@ describe('pipe()', () => {
223248
const getText = $el => $el.text()
224249

225250
beforeEach(() => {
226-
cy.visit('/')
251+
cy.visit('/cypress/fixtures/')
227252
})
228253

229254
it('should have the correct element in the Command Log', () => {
@@ -256,7 +281,7 @@ describe('pipe()', () => {
256281
lastLog = log
257282
}
258283
})
259-
284+
260285
cy.get('body')
261286
.pipe(getFirst)
262287
.should(() => {
@@ -266,7 +291,7 @@ describe('pipe()', () => {
266291
expect(consoleProps.Yielded).to.have.id('first')
267292
})
268293
})
269-
294+
270295
it('should wait to continue for each step and resolve the chain with the correct value', () => {
271296
cy.get('body')
272297
.pipe(getFirst)

Diff for: failure.png

405 KB
Loading

Diff for: index.js

+34-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,34 @@
33
const isJquery = obj =>
44
!!(obj && obj.jquery && Cypress._.isFunction(obj.constructor))
55

6+
const traversals = "find filter not children eq closest first last next nextAll nextUntil parent parents parentsUntil prev prevAll prevUntil siblings".split(" ")
7+
8+
/**
9+
* Patch the provided jQuery collection to add a `.selector` property to all traversal methods just
10+
* like Cypress does in https://github.com/cypress-io/cypress/blob/13ebb9779d21238cae4da4b63cf6230da4a5341d/packages/driver/src/cy/commands/traversals.coffee#L50
11+
* This patch allows a failure to log an error message that includes the last selector used when
12+
* failing to find an element. A nice touch!
13+
* @param {JQueryStatic} $el
14+
*/
15+
const patchJQueryForSelectorProperty = ($el) => {
16+
traversals.forEach(traversal => {
17+
const originalFn = $el.fn[traversal]
18+
19+
$el.fn[traversal] = function (...args) {
20+
const ret = originalFn.apply(this, args)
21+
ret.selector = Cypress._.chain(args)
22+
.reject(Cypress._.isFunction)
23+
.reject(Cypress._.isObject)
24+
.reject(a => a == null)
25+
.value()
26+
.join(', ')
27+
return ret
28+
}
29+
})
30+
}
31+
32+
patchJQueryForSelectorProperty(Cypress.$)
33+
634
const getElements = $el => {
735
if (!$el && !$el.length) {
836
return
@@ -19,7 +47,7 @@ const getElements = $el => {
1947

2048
/**
2149
* Format the argument according to its type
22-
* @param {any} arg
50+
* @param {any} arg
2351
*/
2452
function formatArg (arg) {
2553
switch (typeof arg) {
@@ -32,11 +60,15 @@ function formatArg (arg) {
3260

3361
Cypress.Commands.add('pipe', { prevSubject: true }, (subject, fn, options = { }) => {
3462

63+
// if (isJquery(subject)) {
64+
// patchJQueryForSelectorProperty(subject);
65+
// }
66+
3567
const getEl = (value) => isJquery(value) ? value : isJquery(subject) ? subject : undefined
3668

3769
const now = performance.now()
3870
let isCy = false
39-
71+
4072
Cypress._.defaults(options, {
4173
log: true,
4274
timeout: 4000,

Diff for: package.json

+2-6
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
"description": "Create custom commands using plain-old functions",
55
"main": "index.js",
66
"scripts": {
7-
"start": "http-server . -p 3333",
8-
"test": "concurrently -k --success first 'npm run start' 'wait-on http://localhost:3333 && cypress run'"
7+
"test": "cypress run'"
98
},
109
"repository": {
1110
"type": "git",
@@ -29,9 +28,6 @@
2928
},
3029
"homepage": "https://github.com/NicholasBoll/cypress-pipe#readme",
3130
"devDependencies": {
32-
"concurrently": "^4.1.0",
33-
"cypress": "^3.2.0",
34-
"http-server": "^0.11.1",
35-
"wait-on": "^3.1.0"
31+
"cypress": "^3.8.1"
3632
}
3733
}

0 commit comments

Comments
 (0)