@@ -4,6 +4,7 @@ import {getContainer} from './utils'
4
4
const getDefaultCommandOptions = ( ) => {
5
5
return {
6
6
timeout : Cypress . config ( ) . defaultCommandTimeout ,
7
+ fallbackToPreviousFunctionality : true ,
7
8
log : true ,
8
9
}
9
10
}
@@ -37,27 +38,24 @@ const queryCommands = queryQueryNames.map(queryName => {
37
38
} )
38
39
39
40
const findCommands = findQueryNames . map ( queryName => {
40
- // dom-testing-library find* queries use a promise to look for an element, but that doesn't work well with Cypress retryability
41
- // Use the query* commands so that we can lean on Cypress to do the retry for us
42
- // When it does return a null or empty array, Cypress will retry until the assertions are satisfied or the command times out
43
41
return createCommand ( queryName , queryName . replace ( findRegex , 'get' ) )
44
42
} )
45
43
46
44
function createCommand ( queryName , implementationName ) {
47
45
return {
48
46
name : queryName ,
49
- command : ( ...args ) => {
47
+ options : { prevSubject : [ 'optional' , 'document' , 'element' , 'window' ] } ,
48
+ command : ( prevSubject , ...args ) => {
50
49
const lastArg = args [ args . length - 1 ]
51
50
const defaults = getDefaultCommandOptions ( )
52
51
const options =
53
52
typeof lastArg === 'object' ? { ...defaults , ...lastArg } : defaults
54
53
55
54
const queryImpl = queries [ implementationName ]
56
- const baseCommandImpl = doc => {
57
- const container = getContainer ( options . container || doc )
58
- return queryImpl ( container , ...args )
55
+ const baseCommandImpl = container => {
56
+ return queryImpl ( getContainer ( container ) , ...args )
59
57
}
60
- const commandImpl = doc => baseCommandImpl ( doc )
58
+ const commandImpl = container => baseCommandImpl ( container )
61
59
62
60
const inputArr = args . filter ( filterInputs )
63
61
@@ -70,7 +68,7 @@ function createCommand(queryName, implementationName) {
70
68
input : inputArr ,
71
69
Selector : getSelector ( ) ,
72
70
'Applied To' : getContainer (
73
- options . container || win . document ,
71
+ options . container || prevSubject || win . document ,
74
72
)
75
73
}
76
74
@@ -82,8 +80,8 @@ function createCommand(queryName, implementationName) {
82
80
} )
83
81
}
84
82
85
- const getValue = ( ) => {
86
- const value = commandImpl ( win . document )
83
+ const getValue = ( container = options . container || prevSubject || win . document ) => {
84
+ const value = commandImpl ( container )
87
85
88
86
const result = Cypress . $ ( value )
89
87
if ( value && options . _log ) {
@@ -115,20 +113,35 @@ function createCommand(queryName, implementationName) {
115
113
}
116
114
117
115
let error
116
+ let failedNewFunctionality = false
117
+ let failedOldFunctionality = false
118
118
// Errors will be thrown by @testing -library/dom, but a query might be followed by `.should('not.exist')`
119
119
// We just need to capture the error thrown by @testing -library/dom and return an empty jQuery NodeList
120
120
// to allow Cypress assertions errors to happen naturally. If an assertion fails, we'll have a helpful
121
121
// error message handy to pass on to the user
122
122
const catchQueryError = err => {
123
123
error = err
124
+ failedOldFunctionality = true
124
125
const result = Cypress . $ ( )
125
126
result . selector = getSelector ( )
126
127
return result
127
128
}
128
129
130
+ // Before https://github.com/testing-library/cypress-testing-library/pull/100,
131
+ // queries were run without being scoped to previous subjects. There is code now that depends
132
+ // on functionality before #100. See if we can succeed using old functionality before finally failing
133
+ // This function can be removed as a breaking change
134
+ const catchAndTryOldFunctionality = err => {
135
+ error = err
136
+ failedNewFunctionality = true
137
+ const container = options . fallbackToPreviousFunctionality ? options . container || win . document : undefined
138
+ return getValue ( container )
139
+ }
140
+
129
141
const resolveValue = ( ) => {
130
142
// retry calling "getValue" until following assertions pass or this command times out
131
143
return Cypress . Promise . try ( getValue )
144
+ . catch ( catchAndTryOldFunctionality )
132
145
. catch ( catchQueryError )
133
146
. then ( value => {
134
147
return cy . verifyUpcomingAssertions ( value , options , {
@@ -155,7 +168,11 @@ function createCommand(queryName, implementationName) {
155
168
return subject
156
169
} ) . finally ( ( ) => {
157
170
if ( options . _log ) {
158
- options . _log . end ( )
171
+ if ( failedNewFunctionality && ! failedOldFunctionality ) {
172
+ options . _log . error ( Error ( `@testing-library/cypress will soon only use previous subjects when queries are added to a chain of commands. We've detected an instance where the this functionality failed, but the old functionality passed. Please use cy.${ queryName } (${ queryArgument ( args ) } ) instead of continuing from a previous chain.` ) )
173
+ } else {
174
+ options . _log . end ( )
175
+ }
159
176
}
160
177
} )
161
178
} ,
0 commit comments