From f264eee5f93b7c01b5cf9b69639f328cb41dca48 Mon Sep 17 00:00:00 2001 From: Jeff Avallone Date: Tue, 23 Dec 2014 11:02:15 -0500 Subject: [PATCH] Adding tests for Regexp nodes --- spec/parser/javascript/regexp_spec.js | 312 +++++++++++++++++++++++++- src/js/parser/javascript/regexp.js | 65 +++--- 2 files changed, 344 insertions(+), 33 deletions(-) diff --git a/spec/parser/javascript/regexp_spec.js b/spec/parser/javascript/regexp_spec.js index 40b0160..22facd4 100644 --- a/spec/parser/javascript/regexp_spec.js +++ b/spec/parser/javascript/regexp_spec.js @@ -1,5 +1,315 @@ +import javascript from 'src/js/parser/javascript/parser.js'; +import util from 'src/js/util.js'; +import _ from 'lodash'; +import Snap from 'snapsvg'; +import Q from 'q'; + describe('parser/javascript/regexp.js', function() { - pending(); + _.forIn({ + 'test': { + proxy: jasmine.objectContaining({ textValue: 'test' }) + }, + 'part 1|part 2': { + matches: [ + jasmine.objectContaining({ textValue: 'part 1' }), + jasmine.objectContaining({ textValue: 'part 2' }) + ] + } + }, (content, str) => { + it(`parses "${str}" as a Regexp`, function() { + var parser = new javascript.Parser(str); + expect(parser.__consume__regexp()).toEqual(jasmine.objectContaining(content)); + }); + }); + + describe('#_render', function() { + + beforeEach(function() { + var counter = 0; + + this.node = new javascript.Parser('a|b').__consume__regexp(); + + this.node.container = jasmine.createSpyObj('container', [ + 'addClass', + 'group', + 'prepend', + 'path' + ]); + + this.group = jasmine.createSpyObj('group', [ + 'addClass', + 'transform', + 'group', + 'prepend', + 'path', + 'getBBox' + ]); + this.node.container.group.and.returnValue(this.group); + this.group.addClass.and.returnValue(this.group); + this.group.transform.and.returnValue(this.group); + this.group.getBBox.and.returnValue('group bbox'); + this.group.group.and.callFake(function() { + return `group ${counter++}`; + }); + + this.node.matches = [ + jasmine.createSpyObj('match', ['render']), + jasmine.createSpyObj('match', ['render']), + jasmine.createSpyObj('match', ['render']) + ]; + + this.matchDeferred = [ + Q.defer(), + Q.defer(), + Q.defer() + ]; + + this.node.matches[0].render.and.returnValue(this.matchDeferred[0].promise); + this.node.matches[1].render.and.returnValue(this.matchDeferred[1].promise); + this.node.matches[2].render.and.returnValue(this.matchDeferred[2].promise); + + spyOn(this.node, 'getBBox').and.returnValue('container bbox'); + spyOn(this.node, 'makeCurve').and.returnValue('curve'); + spyOn(this.node, 'makeSide').and.returnValue('side'); + spyOn(this.node, 'makeConnector').and.returnValue('connector'); + + spyOn(util, 'spaceVertically'); + }); + + it('creates a container for the match nodes', function() { + this.node._render(); + expect(this.node.container.group).toHaveBeenCalled(); + expect(this.group.addClass).toHaveBeenCalledWith('regexp-matches'); + expect(this.group.transform).toHaveBeenCalledWith(Snap.matrix() + .translate(20, 0)); + }); + + it('renders each match node', function() { + this.node._render(); + expect(this.node.matches[0].render).toHaveBeenCalledWith('group 0'); + expect(this.node.matches[1].render).toHaveBeenCalledWith('group 1'); + expect(this.node.matches[2].render).toHaveBeenCalledWith('group 2'); + }); + + describe('positioning of the match nodes', function() { + + beforeEach(function() { + this.matchDeferred[0].resolve(); + this.matchDeferred[1].resolve(); + this.matchDeferred[2].resolve(); + }); + + it('spaces the nodes vertically', function(done) { + this.node._render() + .then(() => { + expect(util.spaceVertically).toHaveBeenCalledWith(this.node.matches, { padding: 5 }); + }) + .finally(done) + .done(); + }); + + it('renders the sides and curves into the container', function(done) { + this.node._render() + .then(() => { + expect(this.node.makeCurve).toHaveBeenCalledWith('container bbox', this.node.matches[0]); + expect(this.node.makeCurve).toHaveBeenCalledWith('container bbox', this.node.matches[1]); + expect(this.node.makeCurve).toHaveBeenCalledWith('container bbox', this.node.matches[2]); + expect(this.node.makeSide).toHaveBeenCalledWith('container bbox', this.node.matches[0]); + expect(this.node.makeSide).toHaveBeenCalledWith('container bbox', this.node.matches[2]); + expect(this.node.container.path).toHaveBeenCalledWith('curvecurvecurvesideside'); + }) + .finally(done) + .done(); + }); + + it('renders the connectors into the match container', function(done) { + this.node._render() + .then(() => { + expect(this.node.makeConnector).toHaveBeenCalledWith('group bbox', this.node.matches[0]); + expect(this.node.makeConnector).toHaveBeenCalledWith('group bbox', this.node.matches[1]); + expect(this.node.makeConnector).toHaveBeenCalledWith('group bbox', this.node.matches[2]); + expect(this.group.path).toHaveBeenCalledWith('connectorconnectorconnector'); + }) + .finally(done) + .done(); + }); + + }); + + }); + + describe('#madeSide', function() { + + beforeEach(function() { + this.node = new javascript.Parser('a|b').__consume__regexp(); + + this.containerBox = { + cy: 50, + width: 30 + }; + this.matchBox = { + }; + + this.match = jasmine.createSpyObj('match', ['getBBox']); + this.match.getBBox.and.returnValue(this.matchBox); + }); + + describe('when the match node is 15px or more from the centerline', function() { + + describe('when the match node is above the centerline', function() { + + beforeEach(function() { + this.matchBox.ay = 22; + }); + + it('returns the vertical sideline to the match node', function() { + expect(this.node.makeSide(this.containerBox, this.match)).toEqual([ + 'M0,50q10,0 10,-10V32', + 'M70,50q-10,0 -10,-10V32' + ]); + }); + + }); + + describe('when the match node is below the centerline', function() { + + beforeEach(function() { + this.matchBox.ay = 88; + }); + + it('returns the vertical sideline to the match node', function() { + expect(this.node.makeSide(this.containerBox, this.match)).toEqual([ + 'M0,50q10,0 10,10V78', + 'M70,50q-10,0 -10,10V78' + ]); + }); + + }); + + }); + + describe('when the match node is less than 15px from the centerline', function() { + + beforeEach(function() { + this.matchBox.ay = 44; + }); + + it('returns nothing', function() { + expect(this.node.makeSide(this.containerBox, this.match)).toBeUndefined(); + }); + + }); + + }); + + describe('#makeCurve', function() { + + beforeEach(function() { + this.node = new javascript.Parser('a|b').__consume__regexp(); + + this.containerBox = { + cy: 50, + width: 30 + }; + this.matchBox = {}; + + this.match = jasmine.createSpyObj('match', ['getBBox']); + this.match.getBBox.and.returnValue(this.matchBox); + }); + + describe('when the match node is 15px or more from the centerline', function() { + + describe('when the match node is above the centerline', function() { + + beforeEach(function() { + this.matchBox.ay = 22; + }); + + it('returns the curve to the match node', function() { + expect(this.node.makeCurve(this.containerBox, this.match)).toEqual([ + 'M10,32q0,-10 10,-10', + 'M60,32q0,-10 -10,-10' + ]); + }); + + }); + + describe('when the match node is below the centerline', function() { + + beforeEach(function() { + this.matchBox.ay = 88; + }); + + it('returns the curve to the match node', function() { + expect(this.node.makeCurve(this.containerBox, this.match)).toEqual([ + 'M10,78q0,10 10,10', + 'M60,78q0,10 -10,10' + ]); + }); + + }); + + }); + + describe('when the match node is less than 15px from the centerline', function() { + + describe('when the match node is above the centerline', function() { + + beforeEach(function() { + this.matchBox.ay = 44; + }); + + it('returns the curve to the match node', function() { + expect(this.node.makeCurve(this.containerBox, this.match)).toEqual([ + 'M0,50c10,0 10,-6 20,-6', + 'M70,50c-10,0 -10,-6 -20,-6' + ]); + }); + + }); + + describe('when the match node is below the centerline', function() { + + beforeEach(function() { + this.matchBox.ay = 55; + }); + + it('returns the curve to the match node', function() { + expect(this.node.makeCurve(this.containerBox, this.match)).toEqual([ + 'M0,50c10,0 10,5 20,5', + 'M70,50c-10,0 -10,5 -20,5' + ]); + }); + + }); + + }); + + }); + + describe('#makeConnector', function() { + + beforeEach(function() { + this.node = new javascript.Parser('a|b').__consume__regexp(); + + this.containerBox = { + width: 4 + }; + this.matchBox = { + ay: 1, + ax: 2, + ax2: 3 + }; + + this.match = jasmine.createSpyObj('match', ['getBBox']); + this.match.getBBox.and.returnValue(this.matchBox); + }); + + it('returns a line from the curve to the match node', function() { + expect(this.node.makeConnector(this.containerBox, this.match)).toEqual('M0,1h2M3,1H4'); + }); + + }); }); diff --git a/src/js/parser/javascript/regexp.js b/src/js/parser/javascript/regexp.js index 2574af0..1c14b7e 100644 --- a/src/js/parser/javascript/regexp.js +++ b/src/js/parser/javascript/regexp.js @@ -6,9 +6,7 @@ export default { type: 'regexp', _render() { - var matchContainer; - - matchContainer = this.container.group() + var matchContainer = this.container.group() .addClass('regexp-matches') .transform(Snap.matrix() .translate(20, 0)); @@ -25,72 +23,75 @@ export default { }); containerBox = this.getBBox(); - paths = _.map(this.matches, this.makeConnectorLine.bind(this, containerBox)); + paths = _.map(this.matches, match => { + return this.makeCurve(containerBox, match) + }); - paths.push(this.makeSideLine(containerBox, _.first(this.matches))); - paths.push(this.makeSideLine(containerBox, _.last(this.matches))); + paths.push(this.makeSide(containerBox, _.first(this.matches))); + paths.push(this.makeSide(containerBox, _.last(this.matches))); this.container.prepend( - this.container.path(paths.join(''))); + this.container.path(_(paths).flatten().compact().values().join(''))); + containerBox = matchContainer.getBBox(); + paths = _.map(this.matches, match => { + return this.makeConnector(containerBox, match); + }); matchContainer.prepend( - matchContainer.path(_.map(this.matches, match => { - var box = match.getBBox(), - container = matchContainer.getBBox(); - - return `M0,${box.ay}h${box.ax}M${box.ax2},${box.ay}H${container.width}`; - }).join(''))); + matchContainer.path(paths.join(''))); }); }, - makeSideLine(containerBox, match) { + makeSide(containerBox, match) { var box = match.getBBox(), - direction = box.ay > containerBox.cy ? 1 : -1, distance = Math.abs(box.ay - containerBox.cy); if (distance >= 15) { - let edge = box.ay - 10 * direction, - shift = 10 * direction; + let shift = (box.ay > containerBox.cy) ? 10 : -10, + edge = box.ay - shift; return [ `M0,${containerBox.cy}q10,0 10,${shift}V${edge}`, `M${containerBox.width + 40},${containerBox.cy}q-10,0 -10,${shift}V${edge}` - ].join(''); - } else { - return ''; + ]; } }, - makeConnectorLine(containerBox, match) { + makeCurve(containerBox, match) { var box = match.getBBox(), - direction = box.ay > containerBox.cy ? 1 : -1, distance = Math.abs(box.ay - containerBox.cy); if (distance >= 15) { - let curve = 10 * direction; + let curve = (box.ay > containerBox.cy) ? 10 : -10; return [ `M10,${box.ay - curve}q0,${curve} 10,${curve}`, `M${containerBox.width + 30},${box.ay - curve}q0,${curve} -10,${curve}` - ].join(''); + ]; } else { let anchor = box.ay - containerBox.cy; return [ `M0,${containerBox.cy}c10,0 10,${anchor} 20,${anchor}`, `M${containerBox.width + 40},${containerBox.cy}c-10,0 -10,${anchor} -20,${anchor}` - ].join(''); + ]; } }, - setup() { - this.matches = [this.properties.match] - .concat(_.map(this.properties.alternates.elements, element => { - return element.properties.match; - })); + makeConnector(containerBox, match) { + var box = match.getBBox(); - if (this.matches.length === 1) { - return this.proxy = this.matches[0]; + return `M0,${box.ay}h${box.ax}M${box.ax2},${box.ay}H${containerBox.width}`; + }, + + setup() { + if (this.properties.alternates.elements.length === 0) { + this.proxy = this.properties.match; + } else { + this.matches = [this.properties.match] + .concat(_.map(this.properties.alternates.elements, element => { + return element.properties.match; + })); } } };