11const { assertLogLength } = require ( '../../support/utils' )
22const { $, _ } = Cypress
33
4+ const captureCommands = ( ) => {
5+ const commands = [ ]
6+
7+ let current
8+
9+ cy . on ( 'command:start' , ( command ) => {
10+ current = command
11+ commands . push ( {
12+ name : command . attributes . name ,
13+ snapshots : 0 ,
14+ retries : 0 ,
15+ } )
16+ } )
17+
18+ cy . on ( 'command:retry' , ( ) => {
19+ commands [ commands . length - 1 ] . retries ++
20+ } )
21+
22+ cy . on ( 'snapshot' , ( ) => {
23+ // Snapshots can occur outside the context of a command - for example, `expect(foo).to.exist` without any wrapping cy command.
24+ // So we keep track of the current command when one starts, and if we're not inside that, create an 'empty' command
25+ // for the snapshot to belong to
26+ if ( ! commands . length || current !== cy . state ( 'current' ) ) {
27+ current = null
28+ commands . push ( { name : null , snapshots : 0 , retries : 0 } )
29+ }
30+
31+ commands [ commands . length - 1 ] . snapshots ++
32+ } )
33+
34+ return ( ) => _ . cloneDeep ( commands )
35+ }
36+
437describe ( 'src/cy/commands/assertions' , ( ) => {
538 before ( ( ) => {
639 cy
@@ -10,10 +43,14 @@ describe('src/cy/commands/assertions', () => {
1043 } )
1144 } )
1245
46+ let testCommands
47+
1348 beforeEach ( function ( ) {
1449 const doc = cy . state ( 'document' )
1550
1651 $ ( doc . body ) . empty ( ) . html ( this . body )
52+
53+ testCommands = captureCommands ( )
1754 } )
1855
1956 context ( '#should' , ( ) => {
@@ -29,8 +66,14 @@ describe('src/cy/commands/assertions', () => {
2966 } )
3067
3168 it ( 'returns the subject for chainability' , ( ) => {
32- cy . noop ( { foo : 'bar' } ) . should ( 'deep.eq' , { foo : 'bar' } ) . then ( ( obj ) => {
33- expect ( obj ) . to . deep . eq ( { foo : 'bar' } )
69+ cy
70+ . noop ( { foo : 'bar' } ) . should ( 'deep.eq' , { foo : 'bar' } )
71+ . then ( ( obj ) => {
72+ expect ( testCommands ( ) ) . to . eql ( [
73+ { name : 'noop' , snapshots : 0 , retries : 0 } ,
74+ { name : 'should' , snapshots : 1 , retries : 0 } ,
75+ { name : 'then' , snapshots : 0 , retries : 0 } ,
76+ ] )
3477 } )
3578 } )
3679
@@ -121,11 +164,19 @@ describe('src/cy/commands/assertions', () => {
121164 cy . wrap ( obj ) . then ( ( ) => {
122165 setTimeout ( ( ) => {
123166 obj . foo = 'baz'
124- }
125- , 100 )
167+ } , 100 )
126168
127169 cy . wrap ( obj )
128- } ) . should ( 'deep.eq' , { foo : 'baz' } )
170+ } )
171+ . should ( 'deep.eq' , { foo : 'baz' } )
172+ . then ( ( ) => {
173+ expect ( testCommands ( ) ) . to . containSubset ( [
174+ { name : 'wrap' , snapshots : 1 , retries : 0 } ,
175+ { name : 'then' , snapshots : 0 , retries : 0 } ,
176+ { name : 'wrap' , snapshots : 2 , retries : ( r ) => r > 1 } ,
177+ { name : 'then' , snapshots : 0 , retries : 0 } ,
178+ ] )
179+ } )
129180 } )
130181
131182 // https://github.com/cypress-io/cypress/issues/16006
@@ -151,6 +202,18 @@ describe('src/cy/commands/assertions', () => {
151202 cy . get ( 'button:first' ) . should ( ( $button ) => {
152203 expect ( $button ) . to . have . class ( 'ready' )
153204 } )
205+ . then ( ( ) => {
206+ expect ( testCommands ( ) ) . to . eql ( [
207+ // cy.get() has 2 snapshots, 1 for itself, and 1
208+ // for the .should(...) assertion.
209+
210+ // TODO: Investigate whether or not the 2 commands are
211+ // snapshotted at the same time. If there's no tick between
212+ // them, we could reuse the snapshots
213+ { name : 'get' , snapshots : 2 , retries : 2 } ,
214+ { name : 'then' , snapshots : 0 , retries : 0 } ,
215+ ] )
216+ } )
154217 } )
155218
156219 it ( 'works with regular objects' , ( ) => {
@@ -1475,7 +1538,7 @@ describe('src/cy/commands/assertions', () => {
14751538 done ( )
14761539 } )
14771540
1478- cy . get ( 'div' ) . should ( ( $divs ) => {
1541+ cy . get ( 'div' , { timeout : 100 } ) . should ( ( $divs ) => {
14791542 expect ( $divs , 'Filter should have 1 items' ) . to . have . length ( 1 )
14801543 } )
14811544 } )
@@ -2927,4 +2990,42 @@ describe('src/cy/commands/assertions', () => {
29272990 cy . get ( '.foo' ) . should ( 'not.exist' )
29282991 } )
29292992 } )
2993+
2994+ context ( 'implicit assertions' , ( ) => {
2995+ // https://github.com/cypress-io/cypress/issues/18549
2996+ // A targeted test for the above issue - in the absence of retries, only a single snapshot
2997+ // should be taken.
2998+ it ( 'only snapshots once when failing to find DOM elements and not retrying' , ( done ) => {
2999+ cy . on ( 'fail' , ( err ) => {
3000+ expect ( testCommands ( ) ) . to . eql ( [ {
3001+ name : 'get' ,
3002+ snapshots : 1 ,
3003+ retries : 0 ,
3004+ } ] )
3005+
3006+ done ( )
3007+ } )
3008+
3009+ cy . get ( '.badId' , { timeout : 0 } )
3010+ } )
3011+
3012+ // https://github.com/cypress-io/cypress/issues/18549
3013+ // This issue was also causing two DOM snapshots to be taken every 50ms
3014+ // while waiting for an element to exist. The first test is sufficient to
3015+ // prevent regressions of the specific issue, but this one is intended to
3016+ // more generally assert that retries do not trigger multiple snapshots.
3017+ it ( 'only snapshots once when retrying assertions' , ( done ) => {
3018+ cy . on ( 'fail' , ( err ) => {
3019+ expect ( testCommands ( ) ) . to . containSubset ( [ {
3020+ name : 'get' ,
3021+ snapshots : 1 ,
3022+ retries : ( v ) => v > 1 ,
3023+ } ] )
3024+
3025+ done ( )
3026+ } )
3027+
3028+ cy . get ( '.badId' , { timeout : 1000 } )
3029+ } )
3030+ } )
29303031} )
0 commit comments