"use strict" var o = require("../../ospec/ospec") var domMock = require("../../test-utils/domMock") var vdom = require("../../render/render") o.spec("render", function() { var $window, root, render o.beforeEach(function() { $window = domMock() root = $window.document.createElement("div") render = vdom($window).render }) o("overwrites existing content", function() { var vnodes = [] root.appendChild($window.document.createElement("div")); render(root, vnodes) o(root.childNodes.length).equals(0) }) o("throws on invalid root node", function() { var threw = false try { render(null, []) } catch (e) { threw = true } o(threw).equals(true) }) o("does not enter infinite loop when oninit triggers render and view throws with an object literal component", function(done) { var A = { oninit: init, view: function() {throw new Error("error")} } function run() { render(root, {tag: A}) } function init() { setTimeout(function() { var threwInner = false try {run()} catch (e) {threwInner = true} o(threwInner).equals(false) done() }, 0) } var threwOuter = false try {run()} catch (e) {threwOuter = true} o(threwOuter).equals(true) }) o("does not try to re-initialize a constructibe component whose view has thrown", function() { var oninit = o.spy() var onbeforeupdate = o.spy() function A(){} A.prototype.view = function() {throw new Error("error")} A.prototype.oninit = oninit A.prototype.onbeforeupdate = onbeforeupdate var throwCount = 0 try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(1) o(onbeforeupdate.callCount).equals(0) try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(1) o(onbeforeupdate.callCount).equals(0) }) o("does not try to re-initialize a constructible component whose oninit has thrown", function() { var oninit = o.spy(function(){throw new Error("error")}) var onbeforeupdate = o.spy() function A(){} A.prototype.view = function(){} A.prototype.oninit = oninit A.prototype.onbeforeupdate = onbeforeupdate var throwCount = 0 try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(1) o(onbeforeupdate.callCount).equals(0) try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(1) o(onbeforeupdate.callCount).equals(0) }) o("does not try to re-initialize a constructible component whose constructor has thrown", function() { var oninit = o.spy() var onbeforeupdate = o.spy() function A(){throw new Error("error")} A.prototype.view = function() {} A.prototype.oninit = oninit A.prototype.onbeforeupdate = onbeforeupdate var throwCount = 0 try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(0) o(onbeforeupdate.callCount).equals(0) try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(0) o(onbeforeupdate.callCount).equals(0) }) o("does not try to re-initialize a closure component whose view has thrown", function() { var oninit = o.spy() var onbeforeupdate = o.spy() function A() { return { view: function() {throw new Error("error")}, oninit: oninit, onbeforeupdate: onbeforeupdate } } var throwCount = 0 try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(1) o(onbeforeupdate.callCount).equals(0) try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(1) o(onbeforeupdate.callCount).equals(0) }) o("does not try to re-initialize a closure component whose oninit has thrown", function() { var oninit = o.spy(function() {throw new Error("error")}) var onbeforeupdate = o.spy() function A() { return { view: function() {}, oninit: oninit, onbeforeupdate: onbeforeupdate } } var throwCount = 0 try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(1) o(onbeforeupdate.callCount).equals(0) try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) o(oninit.callCount).equals(1) o(onbeforeupdate.callCount).equals(0) }) o("does not try to re-initialize a closure component whose closure has thrown", function() { function A() { throw new Error("error") } var throwCount = 0 try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) try {render(root, {tag: A})} catch (e) {throwCount++} o(throwCount).equals(1) }) o("lifecycle methods work in keyed children of recycled keyed", function() { var createA = o.spy() var updateA = o.spy() var removeA = o.spy() var createB = o.spy() var updateB = o.spy() var removeB = o.spy() var a = function() { return {tag: "div", key: 1, children: [ {tag: "div", key: 11, attrs: {oncreate: createA, onupdate: updateA, onremove: removeA}}, {tag: "div", key: 12} ]} } var b = function() { return {tag: "div", key: 2, children: [ {tag: "div", key: 21, attrs: {oncreate: createB, onupdate: updateB, onremove: removeB}}, {tag: "div", key: 22} ]} } render(root, a()) render(root, b()) render(root, a()) o(createA.callCount).equals(2) o(updateA.callCount).equals(0) o(removeA.callCount).equals(1) o(createB.callCount).equals(1) o(updateB.callCount).equals(0) o(removeB.callCount).equals(1) }) o("lifecycle methods work in unkeyed children of recycled keyed", function() { var createA = o.spy() var updateA = o.spy() var removeA = o.spy() var createB = o.spy() var updateB = o.spy() var removeB = o.spy() var a = function() { return {tag: "div", key: 1, children: [ {tag: "div", attrs: {oncreate: createA, onupdate: updateA, onremove: removeA}}, ]} } var b = function() { return {tag: "div", key: 2, children: [ {tag: "div", attrs: {oncreate: createB, onupdate: updateB, onremove: removeB}}, ]} } render(root, a()) render(root, b()) render(root, a()) o(createA.callCount).equals(2) o(updateA.callCount).equals(0) o(removeA.callCount).equals(1) o(createB.callCount).equals(1) o(updateB.callCount).equals(0) o(removeB.callCount).equals(1) }) o("update lifecycle methods work on children of recycled keyed", function() { var createA = o.spy() var updateA = o.spy() var removeA = o.spy() var createB = o.spy() var updateB = o.spy() var removeB = o.spy() var a = function() { return {tag: "div", key: 1, children: [ {tag: "div", attrs: {oncreate: createA, onupdate: updateA, onremove: removeA}}, ]} } var b = function() { return {tag: "div", key: 2, children: [ {tag: "div", attrs: {oncreate: createB, onupdate: updateB, onremove: removeB}}, ]} } render(root, a()) render(root, a()) o(createA.callCount).equals(1) o(updateA.callCount).equals(1) o(removeA.callCount).equals(0) render(root, b()) o(createA.callCount).equals(1) o(updateA.callCount).equals(1) o(removeA.callCount).equals(1) render(root, a()) render(root, a()) o(createA.callCount).equals(2) o(updateA.callCount).equals(2) o(removeA.callCount).equals(1) }) o("svg namespace is preserved in keyed diff (#1820)", function(){ // note that this only exerciese one branch of the keyed diff algo var svg = {tag:"svg", children: [ {tag:"g", key: 0}, {tag:"g", key: 1} ]} render(root, [svg]) o(svg.dom.namespaceURI).equals("http://www.w3.org/2000/svg") o(svg.dom.childNodes[0].namespaceURI).equals("http://www.w3.org/2000/svg") o(svg.dom.childNodes[1].namespaceURI).equals("http://www.w3.org/2000/svg") svg = {tag:"svg", children: [ {tag:"g", key: 1, attrs: {x: 1}}, {tag:"g", key: 2, attrs: {x: 2}} ]} render(root, [svg]) o(svg.dom.namespaceURI).equals("http://www.w3.org/2000/svg") o(svg.dom.childNodes[0].namespaceURI).equals("http://www.w3.org/2000/svg") o(svg.dom.childNodes[1].namespaceURI).equals("http://www.w3.org/2000/svg") }) o("the namespace of the root is passed to children", function() { render(root, [{tag: "svg"}]) o(root.childNodes[0].namespaceURI).equals("http://www.w3.org/2000/svg") render(root.childNodes[0], [{tag: "g"}]) o(root.childNodes[0].childNodes[0].namespaceURI).equals("http://www.w3.org/2000/svg") }) })