Angular 2 unit testing: Make ngFor rendered, child components' templates change? - unit-testing

I have something like this in my parent component's template (the parent component is being tested).
<div *ngFor="let data of _dataRows; let i=index">
<child-cmp [data]="data"></child-cmp>
</div>
<button (click)="sortDataRows()"></button>
When I click the button in the test, the data rows are sorted in the parent component (component being tested). But the child components' order on the template is not changed. Without ngFor, the child components do get their templates updated in my code, when a method is called on the parent component being tested.
Here is the sortDataRows() method in my parent component (and yes, everything works in the application!):
private sortDataRows(sortValues: any): void {
this._dataRows.sort(function(a: any, b: any): number{
if (!a[sortValues.fieldName] || !b[sortValues.fieldName]) {
//this will handle sorting of Null or undefined for any type
return this.compareNullvalues(a[sortValues.fieldName], b[sortValues.fieldName], sortValues.ascending);
}
if (a[sortValues.fieldName] instanceof Date || Number.isInteger(a[sortValues.fieldName])) {
return this.compareNumbersOrDates(a[sortValues.fieldName], b[sortValues.fieldName], sortValues.ascending);
} else {
return this.compareStringValues(a[sortValues.fieldName], b[sortValues.fieldName], sortValues.ascending);
}
});
}

This is currently an open bug on Angular - https://github.com/angular/angular/issues/12642

Related

Recompute Ember custom helper on change of a controller property that is passed to the helper

I'm changing the value of a property in my controller and the helper fails to recompute them.
Sample code here:
My template looks like,
{{#if (my-helper info)}}
<span>Warning</span>
{{/if}}
In my controller,
changeAction: function() {
let that = this,
info = that.get("info");
set(info, "showWarning", true);
}
my helper,
import { helper as buildHelper } from '#ember/component/helper';
export default buildHelper(function(params) {
let that = this,
info = that.get("info");
if(info.showWarning ) {
return true;
} else {
return false
}
});
I see several issues with your code:
The template helper seems to get an object as it's first and only position param: {{my-helper info}} while info is { showWarning: true }. A template helper does recompute if the value passed it changes but not if a property of that value changes. A quick fix would be {{my-helper info.showWarning}}.
In your template helper your are trying to access the property on it's this context. As far as I know that's not supported. As you are using a positional param and it's the first one, it's available as first entry inparams array. So your template helper should look like:
export default buildHelper(function([info]) {
if(info.showWarning ) {
return true;
} else {
return false
}
});
What version of Ember are you using? If it's >= 3.1 you don't need to use this.get() in your controller. If you are using Ember < 3.1 you need to use info.get() also in template helper.
But as described before I would not recommend passing an object to a template helper as it's only updated if the object itself is replaced. Changing a property of it is not enough. You might be able to do so using Class-based Helpers but I wouldn't recommend to do so as it's error prune.

Call method from other template after rendering

Using Meteor I got a template which consists of 2 other templates A & B.
A got a <select> Tag and B got a <canvas> on which something will drawn depending on the select of template A.
When the selection changes, A should call someMethod` of B, and this method should get some data from the db and then draw the canvas. How can I achieve this behaviour?
Simply using Template.B.someMethod = function (bla) ... and then in the JS of A calling Template.B.someMethod(hi) causes an error because someMethod accesses the DOM canvas Element of B which is not rendered when A changes for the first time at the loading.
What would be a fitting way to call the method? Or should I merge the two templates together?
no need to combine A and B templates. the purpose of the parent here is to coordinate data changes between A and B. i.e. you won't be calling functions on B, but rather giving it the data it needs so it can respond.
P (the parent) can push into A a function that A calls when its data changes via the select. P can then push that data into B through Meteor's normal reactivity. B can then respond to that change.
e.g.
<template name="P">
{{A changeHandler=getChangeHandler}}
{{B selection=selectedData}}
</template>
P.js:
Template.P.onCreated(function() {
this.data = new ReactiveVar();
});
Template.P.helpers({
getChangeHandler() {
let template = Template.instance();
return function(data) {
template.data.set(data);
}
},
selectedData() {
return Template.instance().data.get();
}
});
A.js:
Template.A.onCreated({
this.changeHandler = new ReactiveVar(Template.currentData().changeHandler); // i think this is right
});
and whenever A data changes:
let changeHandlerFn = template.changeHandler.get();
if (_.isFunction(changeHandlerFn)) {
changeHandlerFn(updatedData);
}
Besides #zim's answer, you can also use a variation of https://stackoverflow.com/a/43687311/5108796:
HTML (actually Spacebars)
<template name="Parent">
{{> Child1 sharedVar1=sharedVar}}
{{> Child2 sharedVar2=sharedVar}}
</template>
JavaScript
import { ReactiveVar } from 'meteor/reactive-var';
// Just initialize the variable. Could also be within the scope of a template.
var myReactiveVar = new ReactiveVar();
Template.Parent.helpers({
// This is what will be sent to Child1 and Child2.
sharedVar: function () {
return myReactiveVar;
}
});
Template.Child1.events({
'change select': function (event, template) {
// This will trigger a re-execution of Child2 autorun.
template.data.sharedVar1.set(myNewValue);
}
});
Template.Child2.onCreated(function () {
var sharedVar2 = this.data.sharedVar2;
this.autorun(function () {
// As usual, this is reactive.
var newValue = sharedVar2.get();
// Perform some operation using newValue…
});
});
(of course you can split these into several JS files)

Trying to expose additional information when using xUnit Assert.Throws

I'm just setting up some first unit tests, and I can't quite see how I'm trying to achieve (with my current test structure) can be done, which means I'm not sure whether my approach to the tests is incorrect, or it's just a limitation on xUnit.
I'm testing my MVC Controllers, and want to ensure that they all provide a ArgumentNullException if they are constructed passing null across as the arguments (they get resolved via Castle in the real world).
So, I've a private field on the Test class:
private IEnumerable<Type> ControllerTypes = typeof(MyBaseController).Assembly.GetTypes().Where(t => IsController(t));
Then, my test method:
[Fact]
public void EnsureControllersThrowIfProvidedWithNull() {
foreach (var controller in ControllerTypes) {
var ctrs = GetConstructorsForType(controller);
if (null == ctrs || !ctrs.Any()) { //if the controller has no constructors, that's fine, we just skip over it
continue;
}
var ctr = ctrs.ElementAt(0);
var ctrParamsAsNull = ctr.GetParameters().Select(p => (object)null);
Assert.Throws<ArgumentNullException>(() => {
ctr.Invoke(ctrParamsAsNull.ToArray());
});
}
}
So this is all working fine, I run the test runner, and one of my Controllers doesn't throw an ArgumentNullException when passed null, great, my test fails, but I don't know which controller it was, from the given output.
I do know how I can debug through the test to see which it is that fails, and can manually go through all my controllers to check which it is, but it would be useful to know which controller it was that failed.
Or am I just using a unit test wrong here?
(Side note, there's another test which ensures there's only 1 public constructor for each controller, so I can be sure I'm targeting the correct constructor when this fires, as long as that first test passed).
Thanks
Note:
There's a flaw in the logic for the test, which means it doesn't fully cover what I was expecting it too, as long as it throws an ArgumentNullException for at least 1 of the arguments, then it will pass the test, which isn't right. However as the arguments are interfaces I can't instantiate a new instance of them. So anyone looking to copy the code for the test, I wouldn't do so. Not looking for a solution to that issue here.
Assert.Throws is only helper method that executes delegate inside try catch block. You don't have to use it and you can replace it with your own implementation. Something like:
[Fact]
public void EnsureControllersThrowIfProvidedWithNull() {
foreach (var controller in ControllerTypes) {
var ctrs = GetConstructorsForType(controller);
if (null == ctrs || !ctrs.Any()) { //if the controller has no constructors, that's fine, we just skip over it
continue;
}
var ctr = ctrs.ElementAt(0);
var ctrParamsAsNull = ctr.GetParameters().Select(p => (object)null);
book ok = false;
try
{
ctr.Invoke(ctrParamsAsNull.ToArray());
}
catch(ArgumentNullException)
{
//you get exception you expected so continue
ok = true;
}
if(!ok)
{
// you didn't get exception so throw your own exception with message that contains controller type name
throw new Exception(String.Format("Ctor on type {0} did not throw ArgumentNullException",controller.Name);
}
}
}
This is only as idea to work on. You can refactor that inside your own static assertion method...

knockoutjs - custom template is binded with parent content

I have a problem using knockoutjs with custom template bindings.
Suppose I have a HTML body like this:
<div id="1">
<div data-bind="template:{name: '2', data: data}"></div>
</div>
<div id="2">
<h3 data-bind="text: caption"></h3>
</div>
JS code looks like this:
var ViewModel2 = function () {
this.caption = ko.observable("Caption");
}
var ViewModel1 = function () {
this.data = new ViewModel2();
}
ko.applyBindings(new ViewModel1(), document.getElementById("1"));
If we test this code, everything will work just fine;
See JSFiddle example: http://jsfiddle.net/4eTWW/33/
Now suppose we want to make our custom template binding. We'll use 'templatex' binding instead of 'template'.
In HTML we need to change just one line:
<div data-bind="templatex:{name: '2', data: data}"></div>
Next, let's add custom template binding to JS:
/*Custom binding*/
ko.bindingHandlers.templatex = {
init: function (element) {
ko.bindingHandlers.template.init.apply(this, arguments);
},
update: ko.bindingHandlers.template.update
}
See: http://jsfiddle.net/4eTWW/35/
But in this case we have an error, saying that it can't find 'caption' in the model.
Now let's add template {} to html bindings:
<div data-bind="template: {}, templatex:{name: '2', data: data}"></div>
See: http://jsfiddle.net/4eTWW/36/
And now everything works just fine.
It seems that while binding parent div it can't determine that child div is a template.
So how can I mark it as a template in my custom template binder?
Thanks.
You have wrong update handler, change to this:
ko.bindingHandlers.templatex= {
init: function(element) {
// do things
return ko.bindingHandlers.template.init.apply(this, arguments);
},
update: function(element) {
return ko.bindingHandlers.template.update.apply(this, arguments);
}
}
Here is working fiddle: http://jsfiddle.net/vyshniakov/4eTWW/39/
I don't think you can use a custom binding to create a new template engine. You need to register your custom engine with ko.setTemplateEngine().
From the knockoutjs source:
If you want to make a custom template engine,
[1] Inherit from the ko.templateEngine class (like ko.nativeTemplateEngine does)
[2] Override 'renderTemplateSource', supplying a function with this signature:
function (templateSource, bindingContext, options) {
// - templateSource.text() is the text of the template you should render
// - bindingContext.$data is the data you should pass into the template
// - you might also want to make bindingContext.$parent, bindingContext.$parents,
// and bindingContext.$root available in the template too
// - options gives you access to any other properties set on "data-bind: { template: options }"
//
// Return value: an array of DOM nodes
}
[3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
function (script) {
// Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
// For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
}
This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
and then you don't need to override 'createJavaScriptEvaluatorBlock'.
Example: http://jsfiddle.net/6pStz/ (see Note 7 on this page)

Kohana template controller, no auto rendering

I have a controller (Controller_Product) that extends Controller_Template.
In the Controller_Product I have some actions (create, edit, etc.) where I need the template to be rendered, but some actions (ex. save, delete) have to return a json object, so I don't need the template to be rendered.
How can I solve this problem?
I can set the $this->auto_render to FALSE in my save or delete action, but the template will be created in this case too, even if will be no rendered. I think this is not very elegant to load a template when I don't actually need it.
Any suggestions?
Something along these lines perhaps:
public function before()
{
if (in_array($this->request->action(), array('save', 'delete')))
{
$this->auto_render = FALSE;
}
parent::before();
}
[edit]
A better approach might be to check for an ajax request:
public function before()
{
if ($this->request->is_ajax())
{
$this->auto_render = FALSE;
}
parent::before();
}