forked from cypress-io/cypress
/
angular.ts
126 lines (99 loc) · 3.39 KB
/
angular.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import _ from 'lodash'
import $ from 'jquery'
import Promise from 'bluebird'
import $errUtils from '../../cypress/error_utils'
const ngPrefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-']
interface InternalNgOptions extends Partial<Cypress.Loggable & Cypress.Timeoutable> {
_log?: any
}
export default (Commands, Cypress, cy, state) => {
const findByNgBinding = (binding, options) => {
const selector = '.ng-binding'
const { angular } = state('window')
_.extend(options, { verify: false, log: false })
const getEl = ($elements) => {
const filtered = $elements.filter((index, el) => {
const dataBinding = angular.element(el).data('$binding')
if (dataBinding) {
const bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding
return bindingName.includes(binding)
}
})
// if we have items return
// those filtered items
if (filtered.length) {
return filtered
}
// else return null element
return $(null as any) // cast to any to satisfy typescript
}
const resolveElements = () => {
return cy.now('get', selector, options).then(($elements) => {
return cy.verifyUpcomingAssertions(getEl($elements), options, {
onRetry: resolveElements,
onFail (err) {
err.message = `Could not find element for binding: '${binding}'.`
},
})
})
}
return resolveElements()
}
const findByNgAttr = (name, attr, el, options) => {
const selectors: string[] = []
let error = `Could not find element for ${name}: '${el}'. Searched `
_.extend(options, { verify: false, log: false })
const finds = _.map(ngPrefixes, (prefix) => {
const selector = `[${prefix}${attr}'${el}']`
selectors.push(selector)
const resolveElements = () => {
return cy.now('get', selector, options).then(($elements) => {
return cy.verifyUpcomingAssertions($elements, options, {
onRetry: resolveElements,
})
})
}
return resolveElements()
})
error += `${selectors.join(', ')}.`
const cancelAll = () => {
return _.invokeMap(finds, 'cancel')
}
return Promise
.any(finds)
.then((subject) => {
cancelAll()
return subject
}).catch(Promise.AggregateError, () => {
return $errUtils.throwErr(error)
})
}
Commands.addAll({
ng (type, selector, userOptions: Partial<Cypress.Loggable & Cypress.Timeoutable> = {}) {
// what about requirejs / browserify?
// we need to intelligently check to see if we're using those
// and if angular is available through them. throw a very specific
// error message here that's different depending on what module
// system you're using
if (!state('window').angular) {
$errUtils.throwErrByPath('ng.no_global')
}
const options: InternalNgOptions = _.defaults({}, userOptions, { log: true })
if (options.log) {
options._log = Cypress.log({
timeout: options.timeout,
})
}
switch (type) {
case 'model':
return findByNgAttr('model', 'model=', selector, options)
case 'repeater':
return findByNgAttr('repeater', 'repeat*=', selector, options)
case 'binding':
return findByNgBinding(selector, options)
default:
return
}
},
})
}