Final fixes for #670 - Add support for serialized globals and retain globals on re-render

This commit is contained in:
Patrick Steele-Idem 2017-04-17 16:11:07 -06:00
parent 25c779024c
commit 3d1ceab41d
21 changed files with 182 additions and 183 deletions

View File

@ -3,6 +3,7 @@
var domInsert = require('../runtime/dom-insert');
var marko = require('../');
var getComponentsContext = require('./ComponentsContext').$__getComponentsContext;
var componentsUtil = require('./util');
var componentLookup = componentsUtil.$__componentLookup;
var emitLifecycleEvent = componentsUtil.$__emitLifecycleEvent;
@ -16,7 +17,6 @@ var inherit = require('raptor-util/inherit');
var updateManager = require('./update-manager');
var morphdom = require('../morphdom');
var eventDelegation = require('./event-delegation');
var ComponentsContext = require('./ComponentsContext');
var slice = Array.prototype.slice;
@ -520,10 +520,6 @@ Component.prototype = componentProto = {
out.sync();
out.$__document = self.$__document;
var componentsContext = ComponentsContext.$__getComponentsContext(out);
componentsContext.$__rerenderComponent = self;
componentsContext.$__isRerenderInBrowser = isRerenderInBrowser;
if (isRerenderInBrowser === true) {
out.e =
out.be =
@ -536,6 +532,11 @@ Component.prototype = componentProto = {
outNoop;
}
var componentsContext = getComponentsContext(out);
var globalComponentsContext = componentsContext.$__globalContext;
globalComponentsContext.$__rerenderComponent = self;
globalComponentsContext.$__isRerenderInBrowser = isRerenderInBrowser;
renderer(input, out);
var result = new RenderResult(out);
@ -549,29 +550,28 @@ Component.prototype = componentProto = {
while(targetEl) {
var id = targetEl.id;
if (id) {
fromEl = fromEls[id];
if (fromEl) {
morphdom(
fromEl,
targetEl,
globalComponentsContext,
onNodeAdded,
onBeforeElUpdated,
onBeforeNodeDiscarded,
onNodeDiscarded,
onBeforeElChildrenUpdated);
}
if (id) {
fromEl = fromEls[id];
if (fromEl) {
morphdom(
fromEl,
targetEl,
globalComponentsContext,
onNodeAdded,
onBeforeElUpdated,
onBeforeNodeDiscarded,
onNodeDiscarded,
onBeforeElChildrenUpdated);
}
targetEl = targetEl.nextSibling;
targetEl = targetEl.nextSibling;
}
}
}
result.afterInsert(doc);
out.emit('$__componentsInitialized');
out.data.components = null;
});
this.$__reset();

View File

@ -1,8 +1,6 @@
'use strict';
var nextRepeatedId = require('./nextRepeatedId');
var repeatedRegExp = /\[\]$/;
var componentUtil = require('./util');
var nextComponentId = componentUtil.$__nextComponentId;
var attachBubblingEvent = componentUtil.$__attachBubblingEvent;
var extend = require('raptor-util/extend');
@ -11,8 +9,8 @@ var extend = require('raptor-util/extend');
* a single component and this information is used to instantiate the component
* later (after the rendered HTML has been added to the DOM)
*/
function ComponentDef(component, componentId, out, componentStack, componentStackLen) {
this.$__out = out; // The AsyncWriter that this component is associated with
function ComponentDef(component, componentId, globalComponentsContext, componentStack, componentStackLen) {
this.$__globalComponentsContext = globalComponentsContext; // The AsyncWriter that this component is associated with
this.$__componentStack = componentStack;
this.$__componentStackLen = componentStackLen;
this.$__component = component;
@ -20,7 +18,7 @@ function ComponentDef(component, componentId, out, componentStack, componentStac
this.$__roots = null; // IDs of root elements if there are multiple root elements
this.$__children = null; // An array of nested ComponentDef instances
this.$__domEvents = null; // An array of DOM events that need to be added (in sets of three)
this.$__domEvents = undefined; // An array of DOM events that need to be added (in sets of three)
this.$__isExisting = false;
@ -61,7 +59,7 @@ ComponentDef.prototype = {
return id;
} else {
if (typeof nestedId == 'string' && repeatedRegExp.test(nestedId)) {
return nextRepeatedId(this.$__out, id, nestedId);
return this.$__globalComponentsContext.$__nextRepeatedId(id, nestedId);
} else {
return id + '-' + nestedId;
}
@ -89,12 +87,12 @@ ComponentDef.prototype = {
/**
* Returns the next auto generated unique ID for a nested DOM element or nested DOM component
*/
$__nextId: function() {
$__nextComponentId: function() {
var id = this.id;
return id ?
id + '-c' + (this.$__nextIdIndex++) :
nextComponentId(this.$__out);
return id === null ?
this.$__globalComponentsContext.$__nextComponentId(this.$__out) :
id + '-c' + (this.$__nextIdIndex++);
},
d: function(handlerMethodName, extraArgs) {
@ -102,7 +100,7 @@ ComponentDef.prototype = {
}
};
ComponentDef.$__deserialize = function(o, types, registry) {
ComponentDef.$__deserialize = function(o, types, globals, registry) {
var id = o[0];
var typeName = types[o[1]];
var input = o[2];
@ -145,6 +143,8 @@ ComponentDef.$__deserialize = function(o, types, registry) {
component.$__setCustomEvents(customEvents, scope);
}
component.$__global = globals;
return {
$__component: component,
$__roots: extra.r,

View File

@ -2,7 +2,8 @@
var ComponentDef = require('./ComponentDef');
var initComponents = require('./init-components');
var isServer = require('./util').$__isServer === true;
var componentsUtil = require('./util');
var isServer = componentsUtil.$__isServer === true;
var EMPTY_OBJECT = {};
@ -12,6 +13,9 @@ function GlobalComponentsContext(out) {
this.$__preservedBodies = EMPTY_OBJECT;
this.$__componentsById = {};
this.$__out = out;
this.$__rerenderComponent = undefined;
this.$__nextIdLookup = null;
this.$__nextComponentId = componentsUtil.$__nextComponentIdProvider(out);
}
GlobalComponentsContext.prototype = {
@ -32,6 +36,10 @@ GlobalComponentsContext.prototype = {
this.$__roots = null;
// Reset things stored in global since global is retained for
// future renders
this.$__out.global.components = undefined;
return topLevelComponentDefs;
},
$__preserveDOMNode: function(elId, bodyOnly) {
@ -44,6 +52,19 @@ GlobalComponentsContext.prototype = {
}
}
preserved[elId] = true;
},
$__nextRepeatedId: function(parentId, id) {
var nextIdLookup = this.$__nextIdLookup || (this.$__nextIdLookup = {});
var indexLookupKey = parentId + '-' + id;
var currentIndex = nextIdLookup[indexLookupKey];
if (currentIndex == null) {
currentIndex = nextIdLookup[indexLookupKey] = 0;
} else {
currentIndex = ++nextIdLookup[indexLookupKey];
}
return indexLookupKey.slice(0, -2) + '[' + currentIndex + ']';
}
};
@ -53,13 +74,13 @@ function ComponentsContext(out, parentComponentsContext, shouldAddGlobalRoot) {
var globalComponentsContext;
if (parentComponentsContext === undefined) {
root = new ComponentDef(null, null, out);
globalComponentsContext = out.global.components;
if (globalComponentsContext === undefined) {
out.global.components = globalComponentsContext = new GlobalComponentsContext(out);
}
root = new ComponentDef(null, null, globalComponentsContext);
if (shouldAddGlobalRoot !== false) {
globalComponentsContext.$__roots.push(root);
}
@ -72,7 +93,6 @@ function ComponentsContext(out, parentComponentsContext, shouldAddGlobalRoot) {
this.$__globalContext = globalComponentsContext;
this.$__out = out;
this.$__componentStack = [root];
this.$__rerenderComponent = undefined;
}
ComponentsContext.prototype = {
@ -82,29 +102,23 @@ ComponentsContext.prototype = {
$__beginComponent: function(component, isSplitComponent) {
var componentStack = this.$__componentStack;
var origLength = componentStack.length;
var parent = componentStack[origLength - 1];
var parentComponentDef = componentStack[origLength - 1];
var componentId = component.id;
if (!componentId) {
componentId = component.id = parent.$__nextId();
}
var out = this.$__out;
var componentDef = new ComponentDef(component, componentId, out, componentStack, origLength);
var componentDef = new ComponentDef(component, componentId, this.$__globalContext, componentStack, origLength);
if (isServer) {
// On the server
if (parent.$__willRerenderInBrowser === true) {
if (parentComponentDef.$__willRerenderInBrowser === true) {
componentDef.$__willRerenderInBrowser = true;
} else {
parent.$__addChild(componentDef);
if (isSplitComponent === false && out.global.noBrowserRerender !== true) {
parentComponentDef.$__addChild(componentDef);
if (isSplitComponent === false && this.$__out.global.noBrowserRerender !== true) {
componentDef.$__willRerenderInBrowser = true;
}
}
} else {
parent.$__addChild(componentDef);
parentComponentDef.$__addChild(componentDef);
this.$__globalContext.$__componentsById[componentId] = componentDef;
}
@ -115,8 +129,8 @@ ComponentsContext.prototype = {
$__nextComponentId: function() {
var componentStack = this.$__componentStack;
var parent = componentStack[componentStack.length - 1];
return parent.$__nextId();
var parentComponentDef = componentStack[componentStack.length - 1];
return parentComponentDef.$__nextComponentId();
}
};

View File

@ -101,9 +101,10 @@ function flattenHelper(components, flattened, typesArray, typesLookup) {
function getRenderedComponents(out, shouldIncludeAll) {
var componentDefs;
var globalComponentsContext;
var outGlobal = out.global;
if (shouldIncludeAll === true) {
globalComponentsContext = out.global.components;
globalComponentsContext = outGlobal.components;
if (globalComponentsContext === undefined) {
return undefined;
@ -126,6 +127,7 @@ function getRenderedComponents(out, shouldIncludeAll) {
var flattened = [];
var typesLookup = {};
var typesArray = [];
var serializedGlobals;
if (shouldIncludeAll === true) {
let roots = globalComponentsContext.$__roots;
@ -136,6 +138,18 @@ function getRenderedComponents(out, shouldIncludeAll) {
flattenHelper(children, flattened, typesArray, typesLookup);
}
}
var serializedGlobalsLookup = outGlobal.serializedGlobals;
if (serializedGlobalsLookup) {
serializedGlobals = {};
var keys = Object.keys(serializedGlobalsLookup);
for (let i=0, len=keys.length; i<len; i++) {
let key = keys[i];
if (serializedGlobalsLookup[key] === true) {
serializedGlobals[key] = outGlobal[key];
}
}
}
} else {
flattenHelper(componentDefs, flattened, typesArray, typesLookup);
}
@ -144,7 +158,7 @@ function getRenderedComponents(out, shouldIncludeAll) {
return undefined;
}
return {w: flattened, t: typesArray};
return {w: flattened, t: typesArray, g: serializedGlobals};
}
function writeInitComponentsCode(out, shouldIncludeAll) {

View File

@ -182,12 +182,13 @@ function initServerRendered(renderedComponents, doc) {
var componentDefs = renderedComponents.w;
var typesArray = renderedComponents.t;
var globals = renderedComponents.g || {};
componentDefs.forEach(function(componentDef) {
componentDef = ComponentDef.$__deserialize(componentDef, typesArray, registry);
componentDef = ComponentDef.$__deserialize(componentDef, typesArray, globals, registry);
initComponent(componentDef, doc || defaultDocument);
});
}
exports.$__initClientRendered = initClientRendered;
exports.$__initServerRendered = initServerRendered;
exports.$__initServerRendered = initServerRendered;

View File

@ -40,9 +40,10 @@ function createRendererFunc(templateRenderFunc, componentProps) {
var componentBody;
var componentState;
var componentsContext = ComponentsContext.$__getComponentsContext(out);
var componentsContext = getComponentsContext(out);
var globalComponentsContext = componentsContext.$__globalContext;
var component = componentsContext.$__rerenderComponent;
var component = globalComponentsContext.$__rerenderComponent;
var fakeComponent;
var isRerender = component !== undefined;
var id = assignedId;
@ -53,7 +54,7 @@ function createRendererFunc(templateRenderFunc, componentProps) {
if (component) {
id = component.id;
isExisting = true;
componentsContext.$__rerenderComponent = null;
globalComponentsContext.$__rerenderComponent = null;
} else {
var componentArgs = out.$__componentArgs;
@ -69,12 +70,11 @@ function createRendererFunc(templateRenderFunc, componentProps) {
if (ref != null) {
ref = ref.toString();
}
id = id || resolveComponentKey(out, ref, scope);
id = id || resolveComponentKey(globalComponentsContext, ref, scope);
customEvents = componentArgs[2];
}
}
var componentsContext = getComponentsContext(out);
id = id || componentsContext.$__nextComponentId();
if (registry.$__isServer && typeName) {
@ -145,19 +145,21 @@ function createRendererFunc(templateRenderFunc, componentProps) {
}
}
if (component && isExisting) {
if (isExisting === true) {
if (!component.$__isDirty || !component.shouldUpdate(input, component.$__state)) {
if (customEvents) {
component.$__setCustomEvents(customEvents, scope);
}
preserveComponentEls(component, out, componentsContext);
preserveComponentEls(component, out, globalComponentsContext);
return;
}
}
if (!component) {
fakeComponent = {};
fakeComponent = {
id: id
};
} else {
componentState = component.$__rawState || componentState;
}

View File

@ -1,15 +0,0 @@
var REPEATED_ID_KEY = '$rep';
module.exports = function nextRepeatedId(out, parentId, id) {
var nextIdLookup = out.global[REPEATED_ID_KEY] || (out.global[REPEATED_ID_KEY] = {});
var indexLookupKey = parentId + '-' + id;
var currentIndex = nextIdLookup[indexLookupKey];
if (currentIndex == null) {
currentIndex = nextIdLookup[indexLookupKey] = 0;
} else {
currentIndex = ++nextIdLookup[indexLookupKey];
}
return indexLookupKey.slice(0, -2) + '[' + currentIndex + ']';
};

View File

@ -4,8 +4,6 @@ var emitLifecycleEvent = componentsUtil.$__emitLifecycleEvent;
var ComponentsContext = require('./ComponentsContext');
var getComponentsContext = ComponentsContext.$__getComponentsContext;
var nextRepeatedId = require('./nextRepeatedId');
var repeatedRegExp = /\[\]$/;
var registry = require('./registry');
var copyProps = require('raptor-util/copyProps');
@ -13,14 +11,14 @@ var isServer = componentsUtil.$__isServer === true;
var COMPONENT_BEGIN_ASYNC_ADDED_KEY = '$wa';
function resolveComponentKey(out, key, scope) {
function resolveComponentKey(globalComponentsContext, key, scope) {
if (key[0] == '#') {
return key.substring(1);
} else {
var resolvedId;
if (repeatedRegExp.test(key)) {
resolvedId = nextRepeatedId(out, scope, key);
resolvedId = globalComponentsContext.$__nextRepeatedId(scope, key);
} else {
resolvedId = scope + '-' + key;
}
@ -29,7 +27,7 @@ function resolveComponentKey(out, key, scope) {
}
}
function preserveComponentEls(existingComponent, out, componentsContext) {
function preserveComponentEls(existingComponent, out, globalComponentsContext) {
var rootEls = existingComponent.$__getRootEls({});
for (var elId in rootEls) {
@ -39,7 +37,7 @@ function preserveComponentEls(existingComponent, out, componentsContext) {
// DOM node is matched up correctly when using morphdom.
out.element(el.tagName, { id: elId });
componentsContext.$__globalContext.$__preserveDOMNode(elId); // Mark the element as being preserved (for morphdom)
globalComponentsContext.$__preserveDOMNode(elId); // Mark the element as being preserved (for morphdom)
}
existingComponent.$__reset(); // The component is no longer dirty so reset internal flags
@ -86,9 +84,10 @@ function createRendererFunc(templateRenderFunc, componentProps, renderingLogic)
}
}
var componentsContext = ComponentsContext.$__getComponentsContext(out);
var componentsContext = getComponentsContext(out);
var globalComponentsContext = componentsContext.$__globalContext;
var component = componentsContext.$__rerenderComponent;
var component = globalComponentsContext.$__rerenderComponent;
var isRerender = component !== undefined;
var id = assignedId;
var isExisting;
@ -98,7 +97,7 @@ function createRendererFunc(templateRenderFunc, componentProps, renderingLogic)
if (component) {
id = component.id;
isExisting = true;
componentsContext.$__rerenderComponent = null;
globalComponentsContext.$__rerenderComponent = null;
} else {
var componentArgs = out.$__componentArgs;
@ -115,12 +114,11 @@ function createRendererFunc(templateRenderFunc, componentProps, renderingLogic)
if (key != null) {
key = key.toString();
}
id = id || resolveComponentKey(out, key, scope);
id = id || resolveComponentKey(globalComponentsContext, key, scope);
customEvents = componentArgs[2];
}
}
var componentsContext = getComponentsContext(out);
id = id || componentsContext.$__nextComponentId();
if (isServer) {
@ -180,7 +178,7 @@ function createRendererFunc(templateRenderFunc, componentProps, renderingLogic)
if (isExisting === true) {
if (component.$__isDirty === false || component.shouldUpdate(input, component.$__state) === false) {
preserveComponentEls(component, out, componentsContext);
preserveComponentEls(component, out, globalComponentsContext);
return;
}
}

View File

@ -1,10 +1,9 @@
var ComponentsContext = require('../ComponentsContext');
var getElementById = require('../util').$__getElementById;
module.exports = function render(input, out) {
var componentsContext = ComponentsContext.$__getComponentsContext(out);
var globalComponentsContext = out.global.components;
if (componentsContext.$__isRerenderInBrowser !== true && componentsContext.$__rerenderComponent !== undefined) {
if (globalComponentsContext && globalComponentsContext.$__isRerenderInBrowser !== true && globalComponentsContext.$__rerenderComponent !== undefined) {
var id = input.id;
// See if the DOM node with the given ID already exists.
@ -29,7 +28,7 @@ module.exports = function render(input, out) {
out.element(tagName, { id: id });
}
componentsContext.$__preserveDOMNode(id, bodyOnly);
globalComponentsContext.$__preserveDOMNode(id, bodyOnly);
return;
}
}

View File

@ -96,6 +96,10 @@ function nextComponentId() {
return 'b' + ((markoGlobal.uid)++);
}
function nextComponentIdProvider(out) {
return nextComponentId;
}
function getElementById(doc, id) {
return doc.getElementById(id);
}
@ -104,9 +108,9 @@ function attachBubblingEvent(componentDef, handlerMethodName, extraArgs) {
if (handlerMethodName) {
var id = componentDef.id;
if (extraArgs) {
var isRerenderInBrowser = componentDef.$__out.global.components.$__isRerenderInBrowser;
var isRerenderInBrowser = componentDef.$__globalComponentsContext.$__isRerenderInBrowser;
if (isRerenderInBrowser) {
if (isRerenderInBrowser === true) {
// If we are bootstrapping a page rendered on the server
// we need to put the actual event args on the UI component
// since we will not actually be updating the DOM
@ -146,7 +150,7 @@ exports.$__getComponentForEl = getComponentForEl;
exports.$__emitLifecycleEvent = emitLifecycleEvent;
exports.$__destroyComponentForEl = destroyComponentForEl;
exports.$__destroyElRecursive = destroyElRecursive;
exports.$__nextComponentId = nextComponentId;
exports.$__nextComponentIdProvider = nextComponentIdProvider;
exports.$__getElementById = getElementById;
exports.$__attachBubblingEvent = attachBubblingEvent;
exports.$__getMarkoPropsFromEl = getMarkoPropsFromEl;

View File

@ -1,17 +1,10 @@
var KEY = Symbol();
function nextComponentIdProvider(out) {
var prefix = out.global.componentIdPrefix || 's'; // "s" is for server (we use "b" for the browser)
var nextId = 0;
function UniqueId(out) {
this.prefix = out.global.componentIdPrefix || 's'; // "s" is for server (we use "b" for the browser)
this.nextId = 0;
}
function nextComponentId(out) {
var global = out.global;
var idProvider = global[KEY] ||
(global[KEY] = new UniqueId(out));
return idProvider.prefix + (idProvider.nextId++);
return function nextComponentId() {
return prefix + (nextId++);
};
}
function attachBubblingEvent(componentDef, handlerMethodName, extraArgs) {
@ -44,6 +37,6 @@ function attachBubblingEvent(componentDef, handlerMethodName, extraArgs) {
}
}
exports.$__nextComponentId = nextComponentId;
exports.$__nextComponentIdProvider = nextComponentIdProvider;
exports.$__isServer = true;
exports.$__attachBubblingEvent = attachBubblingEvent;

View File

@ -1 +1 @@
<html><head><title>Welcome Frank</title></head><body><div id="s0">Hello</div><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"w":[["s0",0,{},{"d":null,"b":null}]],"t":["/marko-test$1.0.0/autotests/async-render/components-await-title/components/hello/index.marko"]})||w.$components})()</script></body></html>
<html><head><title>Welcome Frank</title></head><body><div id="s0">Hello</div><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"w":[["s0",0,{},{"_":1}]],"t":["/marko-test$1.0.0/autotests/async-render/components-await-title/components/hello/index.marko"]})||w.$components})()</script></body></html>

View File

@ -0,0 +1,5 @@
class {
}
<div>
<span.name>${out.global.name}</span>
</div>

View File

@ -0,0 +1,12 @@
class {
onCreate() {
this.state = {
count: 1
}
}
}
<div>
<hello/>
<span.count>${state.count}</span>
</div>

View File

@ -0,0 +1,18 @@
var expect = require('chai').expect;
module.exports = function(helpers) {
var component = helpers.mount(require('./index.marko'), {
$global: {
name: 'Frank'
}
});
expect(component.el.querySelector('.name').innerHTML).to.equal('Frank');
expect(component.el.querySelector('.count').innerHTML).to.equal('1');
component.state.count++;
component.update();
expect(component.el.querySelector('.name').innerHTML).to.equal('Frank');
expect(component.el.querySelector('.count').innerHTML).to.equal('2');
};

View File

@ -1,7 +0,0 @@
var foo = require('./path/to/foo');
module.exports = {
onServerCreate(input, out) {
this.foo = foo.getValue(out);
}
};

View File

@ -1,7 +1,11 @@
class {
onCreate(input, out) {
onCreate() {
this.state = {
count: 1
}
}
}
<div>
<span.name>${out.global.name}</span>
<span.count>${state.count}</span>
</div>

View File

@ -4,5 +4,5 @@ class {
}
}
<div>
<app-wrapper/>
<app-hello key="hello"/>
</div>

View File

@ -1,5 +1,12 @@
lasso-page dependencies=data.browserDependencies lasso=data.lasso
$ {
out.global.name = 'Frank';
out.global.serializedGlobals = {
name: true
};
}
<!DOCTYPE html>
html lang="en"
head

View File

@ -4,67 +4,23 @@ var expect = require('chai').expect;
describe(path.basename(__dirname), function() {
it('should allow attributes to not be updated', function() {
var app = window.app;
var noUpdateComponent = app.getComponent('no-update-attr');
var foo = noUpdateComponent.el.getAttribute('data-foo');
expect(foo).to.equal('server');
var helloComponent = app.getComponent('hello');
noUpdateComponent.input = {
name: 'browser'
};
noUpdateComponent.update();
expect(helloComponent.el.querySelector('.name').innerHTML).to.equal('Frank');
expect(helloComponent.el.querySelector('.count').innerHTML).to.equal('1');
expect(foo).to.equal('server');
});
helloComponent.state.count++;
helloComponent.update();
it('should allow a root element to not be updated', function() {
var app = window.app;
var noUpdateComponent = app.getComponent('no-update-el');
expect(helloComponent.el.querySelector('.name').innerHTML).to.equal('Frank');
expect(helloComponent.el.querySelector('.count').innerHTML).to.equal('2');
expect(noUpdateComponent.getNoUpdateEl().getAttribute('data-foo')).to.equal('server');
expect(noUpdateComponent.getNoUpdateEl().innerHTML).to.equal('server');
app.forceUpdate();
app.update();
noUpdateComponent.input = {
name: 'browser'
};
noUpdateComponent.update();
expect(noUpdateComponent.getNoUpdateEl().getAttribute('data-foo')).to.equal('server');
expect(noUpdateComponent.getNoUpdateEl().innerHTML).to.equal('server');
});
it('should allow a nested element to not be updated', function() {
var app = window.app;
var noUpdateComponent = app.getComponent('no-update-el-nested');
expect(noUpdateComponent.getNoUpdateEl().getAttribute('data-foo')).to.equal('server');
expect(noUpdateComponent.getNoUpdateEl().innerHTML).to.equal('server');
noUpdateComponent.input = {
name: 'browser'
};
noUpdateComponent.update();
expect(noUpdateComponent.getNoUpdateEl().getAttribute('data-foo')).to.equal('server');
expect(noUpdateComponent.getNoUpdateEl().innerHTML).to.equal('server');
});
it('should allow a body element to not be updated', function() {
var app = window.app;
var noUpdateComponent = app.getComponent('no-update-body-el');
expect(noUpdateComponent.getNoUpdateEl().getAttribute('data-foo')).to.equal('server');
expect(noUpdateComponent.getNoUpdateEl().innerHTML).to.equal('server');
noUpdateComponent.input = {
name: 'browser'
};
noUpdateComponent.update();
expect(noUpdateComponent.getNoUpdateEl().getAttribute('data-foo')).to.equal('browser');
expect(noUpdateComponent.getNoUpdateEl().innerHTML).to.equal('server');
helloComponent = app.getComponent('hello');
expect(helloComponent.el.querySelector('.name').innerHTML).to.equal('Frank');
expect(helloComponent.el.querySelector('.count').innerHTML).to.equal('2');
});
});