UNPKG

dc

Version:

A multi-dimensional charting library built to work natively with crossfilter and rendered using d3.js

214 lines (199 loc) 6.95 kB
function parseTranslate (actual) { var parts = /translate\((-?[\d\.]*)(?:[, ](.*))?\)/.exec(actual); if (!parts) { return null; } if (parts[2] === undefined) { parts[2] = 0; } expect(parts.length).toEqual(3); return parts; } function parseTranslateRotate (actual) { var parts = /translate\((-?[\d\.]*)(?:[, ](.*))?\)[, ]rotate\((-?[\d\.]*)\)/.exec(actual); if (!parts) { return null; } if (parts[2] === undefined) { parts[2] = 0; } expect(parts.length).toEqual(4); return parts; } function parsePath (path) { // an svg path is a string of any number of letters // each followed by zero or more numbers separated by spaces or commas var instrexp = /([a-z])[^a-z]*/gi, argexp = /(-?\d+(?:\.\d*)?)[, ]*/gi; var match, result = [], die = 99; while ((match = instrexp.exec(path))) { var instr = match[0]; var cmd = {op: match[1], args: []}; argexp.lastIndex = 0; while ((match = argexp.exec(instr))) { cmd.args.push(match[1]); } result.push(cmd); if (!--die) { throw 'give up'; } } return result; } // there doesn't seem to be any way to access jasmine custom matchers function compareWithinDelta (actual, expected, delta) { if (delta === undefined) { delta = 1e-6; } var result = {}; result.pass = actual >= (+expected - delta) && actual <= (+expected + delta); var pre = 'Expected ' + actual + ' to ', post = 'be within [' + (+expected - delta) + '/' + (+expected + delta) + ']'; if (result.pass) { result.message = pre + 'not ' + post; } else { result.message = pre + post; } return result; } // note: to make these reusable as boolean predicates, they only returns the first // failure instead of using expect function compareSubPath (got, wanted, i, j, delta) { for (var k = 0; k !== wanted.length; ++k) { var commandNum = 'path command #' + i + k; if (got[i + k].op.toUpperCase() !== wanted[j + k].op.toUpperCase()) { return { pass: false, message: commandNum + ' actual \'' + got[i + k].op.toUpperCase() + '\' != expected \'' + wanted[j + k].op.toUpperCase() + '\'' }; } if (got[i + k].args.length !== wanted[j + k].args.length) { return { pass: false, message: commandNum + ' number of arguments ' + got[i + k].args.length + ' != expected ' + wanted[j + k].args.length }; } for (var h = 0; h < got[i + k].args.length; ++h) { var result = compareWithinDelta(got[i + k].args[h], wanted[j + k].args[h], delta); if (!result.pass) { result.message = commandNum + ', element ' + h + ': ' + result.message; return result; } } } return {pass: true}; } function comparePaths (actual, expected, delta) { delta = delta || 1; // default delta of 1px var got = parsePath(actual), wanted = parsePath(expected); if (got.length !== wanted.length) { return { pass: false, message: 'actual number of path cmds ' + actual.length + ' did not match expected number ' + expected.length }; } return compareSubPath(got, wanted, 0, 0, delta); } function findSubPath (actual, expected, delta) { delta = delta || 1; // default delta of 1px var got = parsePath(actual), wanted = parsePath(expected), end = got.length - wanted.length; for (var i = 0; i < end; ++i) { var result = compareSubPath(got, wanted, i, 0, delta); if (result.pass) { return result; } } return { pass: false, message: 'did not find expected subpath \'' + expected + '\' in actual path \'' + actual + '\'' }; } // actually number list, but presumably you'd want closeness if you need that function compareIntList (actual, expected) { var aparts = actual.split(/, */), eparts = expected.split(/, */); if (aparts.length !== eparts.length) { return { pass: false, message: 'actual number of list items ' + aparts.length + ' did not match expected number ' + eparts.length }; } for (var i = 0; i < eparts.length; ++i) { if (+aparts[i] !== +eparts[i]) { return { pass: false, message: 'list item[' + i + '] value ' + aparts[i] + ' did not equal expected value ' + eparts[i] }; } } return {pass: true}; } beforeEach(function () { jasmine.addMatchers({ toBeWithinDelta: function (_) { return { compare: compareWithinDelta }; }, // note: all of these custom matchers ignore the possibility of .not.toMatch // (can't imagine how that would be useful here) toMatchTranslate: function () { return { compare: function (actual, x, y, prec) { var parts = parseTranslate(actual); if (!parts) { return {pass: false, message: '\'' + actual + '\' did not match translate(x[,y]) regexp'}; } expect(+parts[1]).toBeCloseTo(x, prec); expect(+parts[2]).toBeCloseTo(y, prec); return {pass: true}; } }; }, toMatchTransRot: function () { return { compare: function (actual, x, y, r, prec) { var parts = parseTranslateRotate(actual); if (!parts) { return {pass: false, message: '\'' + actual + '\' did not match translate(x[,y]),rotate(r) regexp'}; } expect(+parts[1]).toBeCloseTo(x, prec); expect(+parts[2]).toBeCloseTo(y, prec); expect(+parts[3]).toBeCloseTo(r, prec); return {pass: true}; } }; }, toMatchUrl: function () { return { compare: function (actual, url) { var regexp = new RegExp('url\\("?' + url + '"?\\)'); expect(actual).toMatch(regexp); return {pass: true}; } }; }, toMatchPath: function () { return { compare: comparePaths }; }, toContainPath: function () { return { compare: findSubPath }; }, toEqualIntList: function () { return { compare: compareIntList }; } }); });