function roundNumbers(value, digits) { // Round numbers to |digits| decimal places. return value. replace(/-?\d*\.\d+(e-?\d+)?/g, function(n) { return (parseFloat(n).toFixed(digits)). replace(/\.\d+/, function(m) { return m.replace(/0+$/, ''); }). replace(/\.$/, ''). replace(/^-0$/, '0'); }); } function normalizeValue(value, digits) { // Round numbers and place whitespace between tokens. return roundNumbers(value, digits). replace(/([\w\d.]+|[^\s])/g, '$1 '). replace(/\s+/g, ' '); } // Transform a path seg list into a path string, rounding numbers to |digits| // decimal places. function serializePathSegList(list, digits) { function segmentArguments(segment) { const kCommandDescriptor = { 'M': ['x', 'y'], 'L': ['x', 'y'], 'C': ['x1', 'y1', 'x2', 'y2', 'x', 'y'], 'Q': ['x1', 'y1', 'x', 'y'], 'S': ['x2', 'y2', 'x', 'y'], 'T': ['x', 'y'], 'A': ['r1', 'r2', 'angle', 'largeArcFlag', 'sweepFlag', 'x', 'y'], 'H': ['x'], 'V': ['y'], 'Z': [] }; let command = segment.pathSegTypeAsLetter.toUpperCase(); return kCommandDescriptor[command].map(field => { return Number(segment[field]).toFixed(digits); }); } return Array.from(list).map(segment => { let command = segment.pathSegTypeAsLetter; if (command === 'z') command = 'Z'; return [command, ...segmentArguments(segment)].join(' '); }).join(' '); } function normalizeProperty(path_string) { let probePathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); probePathElement.setAttribute('d', path_string); document.documentElement.appendChild(probePathElement); let string = getComputedStyle(probePathElement).getPropertyValue('d'); probePathElement.remove(); return string; } // Assert that the animated path data of |target| matches that of // |expected_path_string|. Numbers will be rounded to 2 decimal places. function assert_animated_path_equals(target, expected_path_string) { const kDecimals = 2; let expected, actual; if ('animatedPathSegList' in target) { let probePathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); probePathElement.setAttribute('d', expected_path_string); expected = serializePathSegList(probePathElement.pathSegList, kDecimals) actual = serializePathSegList(target.animatedPathSegList, kDecimals); } else if ('d' in target.style) { expected = normalizeValue(normalizeProperty(expected_path_string), kDecimals); actual = normalizeValue(getComputedStyle(target).getPropertyValue('d'), kDecimals); } else { assert_unreached('no animated path data'); } assert_equals(actual, expected); }