1
1
import { configure as configureDTL , queries } from '@testing-library/dom'
2
- import { getContainer } from './utils'
2
+ import { getFirstElement } from './utils'
3
3
4
4
function configure ( { fallbackRetryWithoutPreviousSubject, ...config } ) {
5
5
return configureDTL ( config )
@@ -9,66 +9,79 @@ const findRegex = /^find/
9
9
const queryNames = Object . keys ( queries ) . filter ( q => findRegex . test ( q ) )
10
10
11
11
const commands = queryNames . map ( queryName => {
12
- return createCommand ( queryName , queryName . replace ( findRegex , 'get' ) )
12
+ return createQuery ( queryName , queryName . replace ( findRegex , 'get' ) )
13
13
} )
14
14
15
- function createCommand ( queryName , implementationName ) {
15
+ function createQuery ( queryName , implementationName ) {
16
16
return {
17
17
name : queryName ,
18
- options : { prevSubject : [ 'optional' ] } ,
19
- command : ( prevSubject , ...args ) => {
18
+ command ( ...args ) {
20
19
const lastArg = args [ args . length - 1 ]
21
- const defaults = {
22
- timeout : Cypress . config ( ) . defaultCommandTimeout ,
23
- log : true ,
24
- }
25
- const options =
26
- typeof lastArg === 'object' ? { ...defaults , ...lastArg } : defaults
20
+ const options = typeof lastArg === 'object' ? { ...lastArg } : { }
27
21
28
- const queryImpl = queries [ implementationName ]
29
- const baseCommandImpl = container => {
30
- return queryImpl ( getContainer ( container ) , ...args )
31
- }
32
- const commandImpl = container => baseCommandImpl ( container )
22
+ this . set ( 'timeout' , options . timeout )
33
23
24
+ const queryImpl = queries [ implementationName ]
34
25
const inputArr = args . filter ( filterInputs )
35
26
36
- const getSelector = ( ) => `${ queryName } (${ queryArgument ( args ) } )`
37
-
38
- const win = cy . state ( 'window' )
27
+ const selector = `${ queryName } (${ queryArgument ( args ) } )`
39
28
40
29
const consoleProps = {
41
30
// TODO: Would be good to completely separate out the types of input into their own properties
42
31
input : inputArr ,
43
- Selector : getSelector ( ) ,
44
- 'Applied To' : getContainer (
45
- options . container || prevSubject || win . document ,
46
- ) ,
32
+ Selector : selector ,
47
33
}
48
34
49
- if ( options . log ) {
50
- options . _log = Cypress . log ( {
51
- type : prevSubject ? 'child' : 'parent' ,
35
+ const log =
36
+ options . log !== false &&
37
+ Cypress . log ( {
52
38
name : queryName ,
39
+ type :
40
+ this . get ( 'prev' ) . get ( 'chainerId' ) === this . get ( 'chainerId' )
41
+ ? 'child'
42
+ : 'parent' ,
53
43
message : inputArr ,
54
44
timeout : options . timeout ,
55
45
consoleProps : ( ) => consoleProps ,
56
46
} )
57
- }
58
47
59
- const getValue = (
60
- container = options . container || prevSubject || win . document ,
61
- ) => {
62
- const value = commandImpl ( container )
48
+ const withinSubject = cy . state ( 'withinSubjectChain' )
49
+
50
+ let error
51
+ this . set ( 'onFail' , err => {
52
+ if ( error ) {
53
+ err . message = error . message
54
+ }
55
+ } )
56
+
57
+ return subject => {
58
+ const container = getFirstElement (
59
+ options . container ||
60
+ subject ||
61
+ cy . getSubjectFromChain ( withinSubject ) ||
62
+ cy . state ( 'window' ) . document ,
63
+ )
64
+ consoleProps [ 'Applied To' ] = container
65
+
66
+ let value
67
+
68
+ try {
69
+ value = queryImpl ( container , ...args )
70
+ } catch ( e ) {
71
+ error = e
72
+ value = Cypress . $ ( )
73
+ value . selector = selector
74
+ }
63
75
64
76
const result = Cypress . $ ( value )
65
- if ( value && options . _log ) {
66
- options . _log . set ( '$el' , result )
77
+
78
+ if ( value && log ) {
79
+ log . set ( '$el' , result )
67
80
}
68
81
69
82
// Overriding the selector of the jquery object because it's displayed in the long message of .should('exist') failure message
70
83
// Hopefully it makes it clearer, because I find the normal response of "Expected to find element '', but never found it" confusing
71
- result . selector = getSelector ( )
84
+ result . selector = selector
72
85
73
86
consoleProps . elements = result . length
74
87
if ( result . length === 1 ) {
@@ -86,54 +99,6 @@ function createCommand(queryName, implementationName) {
86
99
87
100
return result
88
101
}
89
-
90
- let error
91
-
92
- // Errors will be thrown by @testing -library/dom, but a query might be followed by `.should('not.exist')`
93
- // We just need to capture the error thrown by @testing -library/dom and return an empty jQuery NodeList
94
- // to allow Cypress assertions errors to happen naturally. If an assertion fails, we'll have a helpful
95
- // error message handy to pass on to the user
96
- const catchQueryError = err => {
97
- error = err
98
- const result = Cypress . $ ( )
99
- result . selector = getSelector ( )
100
- return result
101
- }
102
-
103
- const resolveValue = ( ) => {
104
- // retry calling "getValue" until following assertions pass or this command times out
105
- return Cypress . Promise . try ( getValue )
106
- . catch ( catchQueryError )
107
- . then ( value => {
108
- return cy . verifyUpcomingAssertions ( value , options , {
109
- onRetry : resolveValue ,
110
- onFail : ( ) => {
111
- // We want to override Cypress's normal non-existence message with @testing-library/dom's more helpful ones
112
- if ( error ) {
113
- options . error . message = error . message
114
- }
115
- } ,
116
- } )
117
- } )
118
- }
119
-
120
- return resolveValue ( )
121
- . then ( subject => {
122
- // Remove the error that occurred because it is irrelevant now
123
- if ( consoleProps . error ) {
124
- delete consoleProps . error
125
- }
126
- if ( options . _log ) {
127
- options . _log . snapshot ( )
128
- }
129
-
130
- return subject
131
- } )
132
- . finally ( ( ) => {
133
- if ( options . _log ) {
134
- options . _log . end ( )
135
- }
136
- } )
137
102
} ,
138
103
}
139
104
}
0 commit comments