Skip to content

Commit

Permalink
feat: Add more form element serialization (#146)
Browse files Browse the repository at this point in the history
* Update ember input serialization

* Add version to `package.json`

* Add select test
  • Loading branch information
Robdel12 committed Sep 12, 2019
1 parent e387464 commit a31d5b4
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 33 deletions.
95 changes: 63 additions & 32 deletions addon/snapshot.js
Expand Up @@ -20,41 +20,70 @@ function getDoctype() {
return doctype;
}

const FORM_ELEMENTS_SELECTOR = 'input, textarea, select';

function mutateOriginalDOM(dom) {
function createUID($el) {
const ID = `_${Math.random().toString(36).substr(2, 9)}`;

$el.setAttribute('data-percy-element-id', ID)
}

let formNodes = dom.querySelectorAll(FORM_ELEMENTS_SELECTOR)
let formElements = Array.from(formNodes);

// loop through each form element and apply an ID for serialization later
formElements.forEach((elem) => {
if (!elem.getAttribute('data-percy-element-id')) {
createUID(elem)
}
})
}

// Set the property value into the attribute value for snapshotting inputs
function setAttributeValues(dom) {
// List of input types here https://www.w3.org/TR/html5/forms.html#the-input-element

// Limit scope to inputs only as textareas do not retain their value when cloned
let elems = dom.find(
`input[type=text], input[type=search], input[type=tel], input[type=url], input[type=email],
input[type=password], input[type=number], input[type=checkbox], input[type=radio]`
);

percyJQuery(elems).each(function() {
let elem = percyJQuery(this);
switch(elem.attr('type')) {
function setAttributeValues(originalDOM, clonedDOM) {
let formNodes = originalDOM.querySelectorAll(FORM_ELEMENTS_SELECTOR)
let formElements = Array.from(formNodes);

formElements.forEach(elem => {
let inputId = elem.getAttribute('data-percy-element-id')
let selector = `[data-percy-element-id="${inputId}"]`;
let cloneEl = clonedDOM.querySelector(selector)

if(!cloneEl) return;

switch (elem.type) {
case 'checkbox':
case 'radio':
if (elem.is(':checked')) {
elem.attr('checked', '');
if (elem.checked) {
cloneEl.setAttribute('checked', '')
}
break
case 'select-one':
if (elem.selectedIndex !== -1) {
cloneEl.options[elem.selectedIndex].setAttribute('selected', 'true');
}
break
case 'select-multiple':
let selectedOptions = Array.from(elem.selectedOptions); // eslint-disable-line
let clonedOptions = Array.from(cloneEl.options); // eslint-disable-line

if (selectedOptions.length) {
selectedOptions.forEach((option) => {
const matchingOption = clonedOptions.find((cloneOption) => option.text === cloneOption.text)
matchingOption.setAttribute('selected', 'true')
})
}
break;

break
case 'textarea':
// setting text or value does not work but innerHTML does
cloneEl.innerHTML = elem.value
break
default:
elem.attr('value', elem.val());
cloneEl.setAttribute('value', elem.value)
}
});

return dom;
}

// jQuery clone() does not copy textarea contents, so we explicitly do it here.
function setTextareaContent(dom) {
dom.find('textarea').each(function() {
let elem = percyJQuery(this);
elem.text(elem.val());
});

return dom;
})
}

// Copy attributes from Ember's rootElement to the DOM snapshot <body> tag. Some applications rely
Expand Down Expand Up @@ -95,7 +124,9 @@ export function percySnapshot(name, options) {
let scope = options.scope;

// Create a full-page DOM snapshot from the current testing page.
let domCopy = percyJQuery('html').clone();
let dom = percyJQuery('html');
mutateOriginalDOM(dom[0]);
let domCopy = dom.clone();
let bodyCopy = domCopy.find('body');
let testingContainer = domCopy.find('#ember-testing');

Expand All @@ -107,8 +138,8 @@ export function percySnapshot(name, options) {
snapshotRoot = testingContainer;
}

snapshotRoot = setAttributeValues(snapshotRoot);
snapshotRoot = setTextareaContent(snapshotRoot);
// Pass the actual DOM nodes, not the jquery object
setAttributeValues(dom[0], snapshotRoot[0]);

let snapshotHtml = snapshotRoot.html();

Expand Down
1 change: 1 addition & 0 deletions package.json
@@ -1,5 +1,6 @@
{
"name": "ember-percy",
"version": "1.5.1",
"keywords": [
"ember-addon"
],
Expand Down
17 changes: 16 additions & 1 deletion tests/integration/components/dummy-box-test.js
@@ -1,6 +1,6 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, findAll, click } from '@ember/test-helpers';
import { render, findAll, click, fillIn } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { percySnapshot } from 'ember-percy';

Expand Down Expand Up @@ -84,6 +84,21 @@ module('Integration | Component | dummy box', function(hooks) {
percySnapshot('textarea with value');
});

test('it snapshots select values', async function(assert) {
await render(
hbs`<select>
<option value="one">One</option>
<option value="two">Two</option>
</select>
`);

await percySnapshot('select without value');
await fillIn('select', 'two');
await percySnapshot('select with value');

assert.ok(true);
});

test('it handles identical assets with different paths', async function(assert) {
await render(hbs`
{{#dummy-box}}
Expand Down

0 comments on commit a31d5b4

Please sign in to comment.