diff --git a/index.html b/index.html index 9bb30b26ba..80c56a96bb 100644 --- a/index.html +++ b/index.html @@ -151,7 +151,7 @@ - + diff --git a/js/id/actions/copy_entities.js b/js/id/actions/copy_entities.js new file mode 100644 index 0000000000..5c6a02850b --- /dev/null +++ b/js/id/actions/copy_entities.js @@ -0,0 +1,21 @@ +iD.actions.CopyEntities = function(ids, fromGraph) { + var copies = {}; + + var action = function(graph) { + ids.forEach(function(id) { + fromGraph.entity(id).copy(fromGraph, copies); + }); + + for (var id in copies) { + graph = graph.replace(copies[id]); + } + + return graph; + }; + + action.copies = function() { + return copies; + }; + + return action; +}; diff --git a/js/id/actions/copy_entity.js b/js/id/actions/copy_entity.js deleted file mode 100644 index 33a1cd0002..0000000000 --- a/js/id/actions/copy_entity.js +++ /dev/null @@ -1,21 +0,0 @@ -iD.actions.CopyEntity = function(id, fromGraph, deep) { - var newEntities = []; - - var action = function(graph) { - var entity = fromGraph.entity(id); - - newEntities = entity.copy(deep, fromGraph); - - for (var i = 0; i < newEntities.length; i++) { - graph = graph.replace(newEntities[i]); - } - - return graph; - }; - - action.newEntities = function() { - return newEntities; - }; - - return action; -}; diff --git a/js/id/behavior/paste.js b/js/id/behavior/paste.js index ada7c1d901..82dd028daa 100644 --- a/js/id/behavior/paste.js +++ b/js/id/behavior/paste.js @@ -31,30 +31,21 @@ iD.behavior.Paste = function(context) { var extent = iD.geo.Extent(), oldIDs = context.copyIDs(), oldGraph = context.copyGraph(), - newIDs = [], - i, j; + newIDs = []; if (!oldIDs.length) return; - for (i = 0; i < oldIDs.length; i++) { - var oldEntity = oldGraph.entity(oldIDs[i]), - action = iD.actions.CopyEntity(oldEntity.id, oldGraph, true), - newEntities; + var action = iD.actions.CopyEntities(oldIDs, oldGraph); + context.perform(action); - extent._extend(oldEntity.extent(oldGraph)); - context.perform(action); - - // First element in `newEntities` contains the copied Entity, - // Subsequent array elements contain any descendants.. - newEntities = action.newEntities(); - newIDs.push(newEntities[0].id); + var copies = action.copies(); + for (var id in copies) { + var oldEntity = oldGraph.entity(id), + newEntity = copies[id]; - for (j = 0; j < newEntities.length; j++) { - var newEntity = newEntities[j], - tags = _.omit(newEntity.tags, omitTag); - - context.perform(iD.actions.ChangeTags(newEntity.id, tags)); - } + extent._extend(oldEntity.extent(oldGraph)); + newIDs.push(newEntity.id); + context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag))); } // Put pasted objects where mouse pointer is.. diff --git a/js/id/core/entity.js b/js/id/core/entity.js index b2bb88a7b7..2905a391e8 100644 --- a/js/id/core/entity.js +++ b/js/id/core/entity.js @@ -72,10 +72,14 @@ iD.Entity.prototype = { return this; }, - copy: function() { - // Returns an array so that we can support deep copying ways and relations. - // The first array element will contain this.copy, followed by any descendants. - return [iD.Entity(this, {id: undefined, user: undefined, version: undefined})]; + copy: function(resolver, copies) { + if (copies[this.id]) + return copies[this.id]; + + var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined}); + copies[this.id] = copy; + + return copy; }, osmId: function() { diff --git a/js/id/core/relation.js b/js/id/core/relation.js index 761db2a655..e318e8112e 100644 --- a/js/id/core/relation.js +++ b/js/id/core/relation.js @@ -20,31 +20,19 @@ _.extend(iD.Relation.prototype, { type: 'relation', members: [], - copy: function(deep, resolver, replacements) { - var copy = iD.Entity.prototype.copy.call(this); - if (!deep || !resolver || !this.isComplete(resolver)) { - return copy; - } + copy: function(resolver, copies) { + if (copies[this.id]) + return copies[this.id]; - var members = [], - i, oldmember, oldid, newid, children; + var copy = iD.Entity.prototype.copy.call(this, resolver, copies); - replacements = replacements || {}; - replacements[this.id] = copy[0].id; + var members = this.members.map(function(member) { + return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id}); + }); - for (i = 0; i < this.members.length; i++) { - oldmember = this.members[i]; - oldid = oldmember.id; - newid = replacements[oldid]; - if (!newid) { - children = resolver.entity(oldid).copy(true, resolver, replacements); - newid = replacements[oldid] = children[0].id; - copy = copy.concat(children); - } - members.push({id: newid, type: oldmember.type, role: oldmember.role}); - } + copy = copy.update({members: members}); + copies[this.id] = copy; - copy[0] = copy[0].update({members: members}); return copy; }, diff --git a/js/id/core/way.js b/js/id/core/way.js index 7fad36eba3..ea56ba14de 100644 --- a/js/id/core/way.js +++ b/js/id/core/way.js @@ -12,29 +12,19 @@ _.extend(iD.Way.prototype, { type: 'way', nodes: [], - copy: function(deep, resolver) { - var copy = iD.Entity.prototype.copy.call(this); + copy: function(resolver, copies) { + if (copies[this.id]) + return copies[this.id]; - if (!deep || !resolver) { - return copy; - } + var copy = iD.Entity.prototype.copy.call(this, resolver, copies); - var nodes = [], - replacements = {}, - i, oldid, newid, child; - - for (i = 0; i < this.nodes.length; i++) { - oldid = this.nodes[i]; - newid = replacements[oldid]; - if (!newid) { - child = resolver.entity(oldid).copy(); - newid = replacements[oldid] = child[0].id; - copy = copy.concat(child); - } - nodes.push(newid); - } + var nodes = this.nodes.map(function(id) { + return resolver.entity(id).copy(resolver, copies).id; + }); + + copy = copy.update({nodes: nodes}); + copies[this.id] = copy; - copy[0] = copy[0].update({nodes: nodes}); return copy; }, diff --git a/test/index.html b/test/index.html index 890fbef0ec..9349a34a74 100644 --- a/test/index.html +++ b/test/index.html @@ -136,7 +136,7 @@ - + @@ -246,7 +246,7 @@ - + diff --git a/test/index_packaged.html b/test/index_packaged.html index 4be2d344b7..96ff26064a 100644 --- a/test/index_packaged.html +++ b/test/index_packaged.html @@ -42,7 +42,7 @@ - + diff --git a/test/spec/actions/copy_entities.js b/test/spec/actions/copy_entities.js new file mode 100644 index 0000000000..7e9728e276 --- /dev/null +++ b/test/spec/actions/copy_entities.js @@ -0,0 +1,71 @@ +describe("iD.actions.CopyEntities", function () { + it("copies a node", function () { + var a = iD.Node({id: 'a'}), + base = iD.Graph([a]), + head = iD.actions.CopyEntities(['a'], base)(base), + diff = iD.Difference(base, head), + created = diff.created(); + + expect(head.hasEntity('a')).to.be.ok; + expect(created).to.have.length(1); + }); + + it("copies a way", function () { + var a = iD.Node({id: 'a'}), + b = iD.Node({id: 'b'}), + w = iD.Way({id: 'w', nodes: ['a', 'b']}), + base = iD.Graph([a, b, w]), + action = iD.actions.CopyEntities(['w'], base), + head = action(base), + diff = iD.Difference(base, head), + created = diff.created(); + + expect(head.hasEntity('w')).to.be.ok; + expect(created).to.have.length(3); + }); + + it("copies multiple nodes", function () { + var base = iD.Graph([ + iD.Node({id: 'a'}), + iD.Node({id: 'b'}) + ]), + action = iD.actions.CopyEntities(['a', 'b'], base), + head = action(base), + diff = iD.Difference(base, head), + created = diff.created(); + + expect(head.hasEntity('a')).to.be.ok; + expect(head.hasEntity('b')).to.be.ok; + expect(created).to.have.length(2); + }); + + it("copies multiple ways, keeping the same connections", function () { + var base = iD.Graph([ + iD.Node({id: 'a'}), + iD.Node({id: 'b'}), + iD.Node({id: 'c'}), + iD.Way({id: 'w1', nodes: ['a', 'b']}), + iD.Way({id: 'w2', nodes: ['b', 'c']}) + ]), + action = iD.actions.CopyEntities(['w1', 'w2'], base), + head = action(base), + diff = iD.Difference(base, head), + created = diff.created(); + + expect(created).to.have.length(5); + expect(action.copies().w1.nodes[1]).to.eql(action.copies().w2.nodes[0]); + }); + + it("obtains source entities from an alternate graph", function () { + var a = iD.Node({id: 'a'}), + old = iD.Graph([a]), + base = iD.Graph(), + action = iD.actions.CopyEntities(['a'], old), + head = action(base), + diff = iD.Difference(base, head), + created = diff.created(); + + expect(head.hasEntity('a')).not.to.be.ok; + expect(Object.keys(action.copies())).to.have.length(1); + }); +}); diff --git a/test/spec/actions/copy_entity.js b/test/spec/actions/copy_entity.js deleted file mode 100644 index 3b85f3ba97..0000000000 --- a/test/spec/actions/copy_entity.js +++ /dev/null @@ -1,106 +0,0 @@ -describe("iD.actions.CopyEntity", function () { - it("copies a Node and adds it to the graph", function () { - var a = iD.Node({id: 'a'}), - base = iD.Graph([a]), - head = iD.actions.CopyEntity('a', base, false)(base), - diff = iD.Difference(base, head), - created = diff.created(); - - expect(head.hasEntity('a')).to.be.ok; - expect(created).to.have.length(1); - expect(created[0]).to.be.an.instanceof(iD.Node); - }); - - it("shallow copies a Way and adds it to the graph", function () { - var a = iD.Node({id: 'a'}), - b = iD.Node({id: 'b'}), - w = iD.Way({id: 'w', nodes: ['a', 'b']}), - base = iD.Graph([a, b, w]), - head = iD.actions.CopyEntity('w', base, false)(base), - diff = iD.Difference(base, head), - created = diff.created(); - - expect(head.hasEntity('w')).to.be.ok; - expect(created).to.have.length(1); - expect(created[0]).to.be.an.instanceof(iD.Way); - }); - - it("deep copies a Way and child Nodes and adds them to the graph", function () { - var a = iD.Node({id: 'a'}), - b = iD.Node({id: 'b'}), - w = iD.Way({id: 'w', nodes: ['a', 'b']}), - base = iD.Graph([a, b, w]), - head = iD.actions.CopyEntity('w', base, true)(base), - diff = iD.Difference(base, head), - created = diff.created(); - - expect(head.hasEntity('w')).to.be.ok; - expect(created).to.have.length(3); - expect(created[0]).to.be.an.instanceof(iD.Way); - expect(created[1]).to.be.an.instanceof(iD.Node); - expect(created[2]).to.be.an.instanceof(iD.Node); - }); - - it("shallow copies a Relation and adds it to the graph", function () { - var a = iD.Node({id: 'a'}), - b = iD.Node({id: 'b'}), - w = iD.Way({id: 'w', nodes: ['a', 'b']}), - r = iD.Relation({id: 'r', members: [{id: 'w'}]}), - base = iD.Graph([a, b, w, r]), - head = iD.actions.CopyEntity('r', base, false)(base), - diff = iD.Difference(base, head), - created = diff.created(); - - expect(head.hasEntity('r')).to.be.ok; - expect(created).to.have.length(1); - expect(created[0]).to.be.an.instanceof(iD.Relation); - }); - - it("deep copies a Relation, member Ways, and child Nodes and adds them to the graph");//, function () { - // var a = iD.Node({id: 'a'}), - // b = iD.Node({id: 'b'}), - // w = iD.Way({id: 'w', nodes: ['a', 'b']}), - // r = iD.Relation({id: 'r', members: [{id: 'w'}]}), - // base = iD.Graph([a, b, w, r]), - // head = iD.actions.CopyEntity('r', base, true)(base), - // diff = iD.Difference(base, head), - // created = diff.created(); - - // expect(head.hasEntity('r')).to.be.ok; - // expect(created).to.have.length(4); - // expect(created[0]).to.be.an.instanceof(iD.Relation); - // expect(created[1]).to.be.an.instanceof(iD.Way); - // expect(created[2]).to.be.an.instanceof(iD.Node); - // expect(created[3]).to.be.an.instanceof(iD.Node); - // }); - - it("shallow copies from one graph to another", function () { - var a = iD.Node({id: 'a'}), - b = iD.Node({id: 'b'}), - w = iD.Way({id: 'w', nodes: ['a', 'b']}), - source = iD.Graph([a, b, w]), - base = iD.Graph(), - head = iD.actions.CopyEntity('w', source, false)(base), - diff = iD.Difference(base, head), - created = diff.created(); - - expect(created).to.have.length(1); - expect(created[0]).to.be.an.instanceof(iD.Way); - }); - - it("deep copies from one graph to another", function () { - var a = iD.Node({id: 'a'}), - b = iD.Node({id: 'b'}), - w = iD.Way({id: 'w', nodes: ['a', 'b']}), - source = iD.Graph([a, b, w]), - base = iD.Graph(), - head = iD.actions.CopyEntity('w', source, true)(base), - diff = iD.Difference(base, head), - created = diff.created(); - - expect(created).to.have.length(3); - expect(created[0]).to.be.an.instanceof(iD.Way); - expect(created[1]).to.be.an.instanceof(iD.Node); - expect(created[2]).to.be.an.instanceof(iD.Node); - }); -}); diff --git a/test/spec/core/entity.js b/test/spec/core/entity.js index 5a6e0aa79a..50012798f7 100644 --- a/test/spec/core/entity.js +++ b/test/spec/core/entity.js @@ -38,25 +38,43 @@ describe('iD.Entity', function () { describe("#copy", function () { it("returns a new Entity", function () { - var a = iD.Entity(), - result = a.copy(); - expect(result).to.have.length(1); - expect(result[0]).to.be.an.instanceof(iD.Entity); - expect(a).not.to.equal(result[0]); + var n = iD.Entity({id: 'n'}), + result = n.copy(null, {}); + expect(result).to.be.an.instanceof(iD.Entity); + expect(result).not.to.equal(n); + }); + + it("adds the new Entity to input object", function () { + var n = iD.Entity({id: 'n'}), + copies = {}, + result = n.copy(null, copies); + expect(Object.keys(copies)).to.have.length(1); + expect(copies.n).to.equal(result); + }); + + it("returns an existing copy in input object", function () { + var n = iD.Entity({id: 'n'}), + copies = {}, + result1 = n.copy(null, copies), + result2 = n.copy(null, copies); + expect(Object.keys(copies)).to.have.length(1); + expect(result1).to.equal(result2); }); it("resets 'id', 'user', and 'version' properties", function () { - var a = iD.Entity({id: 'n1234', version: 10, user: 'bot-mode'}), - b = a.copy()[0]; - expect(b.isNew()).to.be.ok; - expect(b.version).to.be.undefined; - expect(b.user).to.be.undefined; + var n = iD.Entity({id: 'n', version: 10, user: 'user'}), + copies = {}; + n.copy(null, copies); + expect(copies.n.isNew()).to.be.ok; + expect(copies.n.version).to.be.undefined; + expect(copies.n.user).to.be.undefined; }); it("copies tags", function () { - var a = iD.Entity({id: 'n1234', version: 10, user: 'test', tags: {foo: 'foo'}}), - b = a.copy()[0]; - expect(b.tags).to.deep.equal(a.tags); + var n = iD.Entity({id: 'n', tags: {foo: 'foo'}}), + copies = {}; + n.copy(null, copies); + expect(copies.n.tags).to.equal(n.tags); }); }); diff --git a/test/spec/core/relation.js b/test/spec/core/relation.js index 119df2fa77..1d30f32b0a 100644 --- a/test/spec/core/relation.js +++ b/test/spec/core/relation.js @@ -28,48 +28,47 @@ describe('iD.Relation', function () { describe("#copy", function () { it("returns a new Relation", function () { - var r1 = iD.Relation({id: 'r1'}), - result = r1.copy(), - r2 = result[0]; + var r = iD.Relation({id: 'r'}), + result = r.copy(null, {}); - expect(result).to.have.length(1); - expect(r2).to.be.an.instanceof(iD.Relation); - expect(r1).not.to.equal(r2); + expect(result).to.be.an.instanceof(iD.Relation); + expect(result).not.to.equal(r); }); - it("keeps same members when deep = false", function () { - var a = iD.Node({id: 'a'}), - b = iD.Node({id: 'b'}), - c = iD.Node({id: 'c'}), - w1 = iD.Way({id: 'w1', nodes: ['a','b','c','a']}), - r1 = iD.Relation({id: 'r1', members: [{id: 'w1', role: 'outer'}]}), - graph = iD.Graph([a, b, c, w1, r1]), - result = r1.copy(), - r1_copy = result[0]; + it("adds the new Relation to input object", function () { + var r = iD.Relation({id: 'r'}), + copies = {}, + result = r.copy(null, copies); + expect(Object.keys(copies)).to.have.length(1); + expect(copies.r).to.equal(result); + }); - expect(result).to.have.length(1); - expect(r1.members).to.deep.equal(r1_copy.members); + it("returns an existing copy in input object", function () { + var r = iD.Relation({id: 'r'}), + copies = {}, + result1 = r.copy(null, copies), + result2 = r.copy(null, copies); + expect(Object.keys(copies)).to.have.length(1); + expect(result1).to.equal(result2); }); - it("makes new members when deep = true", function () { + it("deep copies members", function () { var a = iD.Node({id: 'a'}), b = iD.Node({id: 'b'}), c = iD.Node({id: 'c'}), - w1 = iD.Way({id: 'w1', nodes: ['a','b','c','a']}), - r1 = iD.Relation({id: 'r1', members: [{id: 'w1', role: 'outer'}]}), - graph = iD.Graph([a, b, c, w1, r1]), - result = r1.copy(true, graph), - r1_copy = result[0]; - - expect(result).to.have.length(5); - expect(result[0]).to.be.an.instanceof(iD.Relation); - expect(result[1]).to.be.an.instanceof(iD.Way); - expect(result[2]).to.be.an.instanceof(iD.Node); - expect(result[3]).to.be.an.instanceof(iD.Node); - expect(result[4]).to.be.an.instanceof(iD.Node); + w = iD.Way({id: 'w', nodes: ['a','b','c','a']}), + r = iD.Relation({id: 'r', members: [{id: 'w', role: 'outer'}]}), + graph = iD.Graph([a, b, c, w, r]), + copies = {} + result = r.copy(graph, copies); - expect(r1_copy.members[0].id).not.to.equal(r1.members[0].id); - expect(r1_copy.members[0].role).to.equal(r1.members[0].role); + expect(Object.keys(copies)).to.have.length(5); + expect(copies.w).to.be.an.instanceof(iD.Way); + expect(copies.a).to.be.an.instanceof(iD.Node); + expect(copies.b).to.be.an.instanceof(iD.Node); + expect(copies.c).to.be.an.instanceof(iD.Node); + expect(result.members[0].id).not.to.equal(r.members[0].id); + expect(result.members[0].role).to.equal(r.members[0].role); }); it("deep copies non-tree relation graphs without duplicating children", function () { @@ -77,48 +76,39 @@ describe('iD.Relation', function () { r1 = iD.Relation({id: 'r1', members: [{id: 'r2'}, {id: 'w'}]}), r2 = iD.Relation({id: 'r2', members: [{id: 'w'}]}), graph = iD.Graph([w, r1, r2]), - result = r1.copy(true, graph), - r1_copy = result[0], - r2_copy = result[1], - w_copy = result[2]; - - expect(result).to.have.length(3); - expect(r1_copy).to.be.an.instanceof(iD.Relation); - expect(r2_copy).to.be.an.instanceof(iD.Relation); - expect(w_copy).to.be.an.instanceof(iD.Way); - - expect(r1_copy.members[0].id).to.equal(r2_copy.id); - expect(r1_copy.members[1].id).to.equal(r2_copy.members[0].id); - }); - - it("deep copies cyclical relation graphs without issue"); //, function () { - // var r1 = iD.Relation({id: 'r1', members: [{id: 'r2'}]}), - // r2 = iD.Relation({id: 'r2', members: [{id: 'r1'}]}), - // graph = iD.Graph([r1, r2]), - // result = r1.copy(true, graph), - // r1_copy = result[0], - // r2_copy = result[1]; - - // expect(result).to.have.length(2); - // expect(r1_copy).to.be.an.instanceof(iD.Relation); - // expect(r2_copy).to.be.an.instanceof(iD.Relation); - - // var msg = 'r1_copy = ' + JSON.stringify(r1_copy) + - // 'r2_copy = ' + JSON.stringify(r2_copy); - // expect(r1_copy.members[0].id).to.equal(r2_copy.id, msg); - // expect(r2_copy.members[0].id).to.equal(r1_copy.id, msg); - // }); - - it("deep copies self-refrencing relations without issue"); //, function () { - // var r1 = iD.Relation({id: 'r1', members: [{id: 'r1'}]}), - // graph = iD.Graph([r1]), - // result = r1.copy(true, graph), - // r1_copy = result[0]; - - // expect(result).to.have.length(1); - // expect(r1_copy).to.be.an.instanceof(iD.Relation); - // expect(r1_copy.members[0].id).to.equal(r1_copy.id); - // }); + copies = {}; + r1.copy(graph, copies); + + expect(Object.keys(copies)).to.have.length(3); + expect(copies.r1).to.be.an.instanceof(iD.Relation); + expect(copies.r2).to.be.an.instanceof(iD.Relation); + expect(copies.w).to.be.an.instanceof(iD.Way); + expect(copies.r1.members[0].id).to.equal(copies.r2.id); + expect(copies.r1.members[1].id).to.equal(copies.w.id); + expect(copies.r2.members[0].id).to.equal(copies.w.id); + }); + + it("deep copies cyclical relation graphs without issue", function () { + var r1 = iD.Relation({id: 'r1', members: [{id: 'r2'}]}), + r2 = iD.Relation({id: 'r2', members: [{id: 'r1'}]}), + graph = iD.Graph([r1, r2]), + copies = {}; + r1.copy(graph, copies); + + expect(Object.keys(copies)).to.have.length(2); + expect(copies.r1.members[0].id).to.equal(copies.r2.id); + expect(copies.r2.members[0].id).to.equal(copies.r1.id); + }); + + it("deep copies self-referencing relations without issue", function () { + var r = iD.Relation({id: 'r', members: [{id: 'r'}]}), + graph = iD.Graph([r]), + copies = {}; + r.copy(graph, copies); + + expect(Object.keys(copies)).to.have.length(1); + expect(copies.r.members[0].id).to.equal(copies.r.id); + }); }); describe("#extent", function () { diff --git a/test/spec/core/way.js b/test/spec/core/way.js index 0034548049..93fd66e158 100644 --- a/test/spec/core/way.js +++ b/test/spec/core/way.js @@ -28,47 +28,54 @@ describe('iD.Way', function() { describe("#copy", function () { it("returns a new Way", function () { - var w1 = iD.Way({id: 'w1'}), - result = w1.copy(), - w2 = result[0]; + var w = iD.Way({id: 'w'}), + result = w.copy(null, {}); - expect(result).to.have.length(1); - expect(w2).to.be.an.instanceof(iD.Way); - expect(w1).not.to.equal(w2); + expect(result).to.be.an.instanceof(iD.Way); + expect(result).not.to.equal(w); }); - it("keeps same nodes when deep = false", function () { + it("adds the new Way to input object", function () { + var w = iD.Way({id: 'w'}), + copies = {}, + result = w.copy(null, copies); + expect(Object.keys(copies)).to.have.length(1); + expect(copies.w).to.equal(result); + }); + + it("returns an existing copy in input object", function () { + var w = iD.Way({id: 'w'}), + copies = {}, + result1 = w.copy(null, copies), + result2 = w.copy(null, copies); + expect(Object.keys(copies)).to.have.length(1); + expect(result1).to.equal(result2); + }); + + it("deep copies nodes", function () { var a = iD.Node({id: 'a'}), b = iD.Node({id: 'b'}), - c = iD.Node({id: 'c'}), - w1 = iD.Way({id: 'w1', nodes: ['a','b','c','a']}), - graph = iD.Graph([a, b, c, w1]), - result = w1.copy(), - w2 = result[0]; + w = iD.Way({id: 'w', nodes: ['a', 'b']}), + graph = iD.Graph([a, b, w]), + copies = {}, + result = w.copy(graph, copies); - expect(result).to.have.length(1); - expect(w1.nodes).to.deep.equal(w2.nodes); + expect(Object.keys(copies)).to.have.length(3); + expect(copies.a).to.be.an.instanceof(iD.Node); + expect(copies.b).to.be.an.instanceof(iD.Node); + expect(copies.a).not.to.equal(w.nodes[0]); + expect(copies.b).not.to.equal(w.nodes[1]); + expect(result.nodes).to.deep.eql([copies.a.id, copies.b.id]); }); - it("makes new nodes when deep = true", function () { + it("creates only one copy of shared nodes", function () { var a = iD.Node({id: 'a'}), - b = iD.Node({id: 'b'}), - c = iD.Node({id: 'c'}), - w1 = iD.Way({id: 'w1', nodes: ['a','b','c','a']}), - graph = iD.Graph([a, b, c, w1]), - result = w1.copy(true, graph), - w2 = result[0]; - - expect(result).to.have.length(4); - expect(result[0]).to.be.an.instanceof(iD.Way); - expect(result[1]).to.be.an.instanceof(iD.Node); - expect(result[2]).to.be.an.instanceof(iD.Node); - expect(result[3]).to.be.an.instanceof(iD.Node); - - expect(w2.nodes[0]).not.to.equal(w1.nodes[0]); - expect(w2.nodes[1]).not.to.equal(w1.nodes[1]); - expect(w2.nodes[2]).not.to.equal(w1.nodes[2]); - expect(w2.nodes[3]).to.equal(w2.nodes[0]); + w = iD.Way({id: 'w', nodes: ['a', 'a']}), + graph = iD.Graph([a, w]), + copies = {}, + result = w.copy(graph, copies); + + expect(result.nodes[0]).to.equal(result.nodes[1]); }); });