@@ -20,41 +20,70 @@ function getDoctype() {
20
20
return doctype ;
21
21
}
22
22
23
+ const FORM_ELEMENTS_SELECTOR = 'input, textarea, select' ;
24
+
25
+ function mutateOriginalDOM ( dom ) {
26
+ function createUID ( $el ) {
27
+ const ID = `_${ Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) } ` ;
28
+
29
+ $el . setAttribute ( 'data-percy-element-id' , ID )
30
+ }
31
+
32
+ let formNodes = dom . querySelectorAll ( FORM_ELEMENTS_SELECTOR )
33
+ let formElements = Array . from ( formNodes ) ;
34
+
35
+ // loop through each form element and apply an ID for serialization later
36
+ formElements . forEach ( ( elem ) => {
37
+ if ( ! elem . getAttribute ( 'data-percy-element-id' ) ) {
38
+ createUID ( elem )
39
+ }
40
+ } )
41
+ }
42
+
23
43
// Set the property value into the attribute value for snapshotting inputs
24
- function setAttributeValues ( dom ) {
25
- // List of input types here https://www.w3.org/TR/html5/forms.html#the-input-element
26
-
27
- // Limit scope to inputs only as textareas do not retain their value when cloned
28
- let elems = dom . find (
29
- `input[type=text], input[type=search], input[type=tel], input[type=url], input[type=email],
30
- input[type=password], input[type=number], input[type=checkbox], input[type=radio]`
31
- ) ;
32
-
33
- percyJQuery ( elems ) . each ( function ( ) {
34
- let elem = percyJQuery ( this ) ;
35
- switch ( elem . attr ( ' type' ) ) {
44
+ function setAttributeValues ( originalDOM , clonedDOM ) {
45
+ let formNodes = originalDOM . querySelectorAll ( FORM_ELEMENTS_SELECTOR )
46
+ let formElements = Array . from ( formNodes ) ;
47
+
48
+ formElements . forEach ( elem => {
49
+ let inputId = elem . getAttribute ( 'data-percy-element-id' )
50
+ let selector = `[data-percy-element-id=" ${ inputId } "]` ;
51
+ let cloneEl = clonedDOM . querySelector ( selector )
52
+
53
+ if ( ! cloneEl ) return ;
54
+
55
+ switch ( elem . type ) {
36
56
case 'checkbox' :
37
57
case 'radio' :
38
- if ( elem . is ( ':checked' ) ) {
39
- elem . attr ( 'checked' , '' ) ;
58
+ if ( elem . checked ) {
59
+ cloneEl . setAttribute ( 'checked' , '' )
60
+ }
61
+ break
62
+ case 'select-one' :
63
+ if ( elem . selectedIndex !== - 1 ) {
64
+ cloneEl . options [ elem . selectedIndex ] . setAttribute ( 'selected' , 'true' ) ;
65
+ }
66
+ break
67
+ case 'select-multiple' :
68
+ let selectedOptions = Array . from ( elem . selectedOptions ) ; // eslint-disable-line
69
+ let clonedOptions = Array . from ( cloneEl . options ) ; // eslint-disable-line
70
+
71
+ if ( selectedOptions . length ) {
72
+ selectedOptions . forEach ( ( option ) => {
73
+ const matchingOption = clonedOptions . find ( ( cloneOption ) => option . text === cloneOption . text )
74
+ matchingOption . setAttribute ( 'selected' , 'true' )
75
+ } )
40
76
}
41
- break ;
77
+
78
+ break
79
+ case 'textarea' :
80
+ // setting text or value does not work but innerHTML does
81
+ cloneEl . innerHTML = elem . value
82
+ break
42
83
default :
43
- elem . attr ( 'value' , elem . val ( ) ) ;
84
+ cloneEl . setAttribute ( 'value' , elem . value )
44
85
}
45
- } ) ;
46
-
47
- return dom ;
48
- }
49
-
50
- // jQuery clone() does not copy textarea contents, so we explicitly do it here.
51
- function setTextareaContent ( dom ) {
52
- dom . find ( 'textarea' ) . each ( function ( ) {
53
- let elem = percyJQuery ( this ) ;
54
- elem . text ( elem . val ( ) ) ;
55
- } ) ;
56
-
57
- return dom ;
86
+ } )
58
87
}
59
88
60
89
// Copy attributes from Ember's rootElement to the DOM snapshot <body> tag. Some applications rely
@@ -95,7 +124,9 @@ export function percySnapshot(name, options) {
95
124
let scope = options . scope ;
96
125
97
126
// Create a full-page DOM snapshot from the current testing page.
98
- let domCopy = percyJQuery ( 'html' ) . clone ( ) ;
127
+ let dom = percyJQuery ( 'html' ) ;
128
+ mutateOriginalDOM ( dom [ 0 ] ) ;
129
+ let domCopy = dom . clone ( ) ;
99
130
let bodyCopy = domCopy . find ( 'body' ) ;
100
131
let testingContainer = domCopy . find ( '#ember-testing' ) ;
101
132
@@ -107,8 +138,8 @@ export function percySnapshot(name, options) {
107
138
snapshotRoot = testingContainer ;
108
139
}
109
140
110
- snapshotRoot = setAttributeValues ( snapshotRoot ) ;
111
- snapshotRoot = setTextareaContent ( snapshotRoot ) ;
141
+ // Pass the actual DOM nodes, not the jquery object
142
+ setAttributeValues ( dom [ 0 ] , snapshotRoot [ 0 ] ) ;
112
143
113
144
let snapshotHtml = snapshotRoot . html ( ) ;
114
145
0 commit comments