Ember tests: checkbox - ember.js

I'm developing ember tests and I want to check if error messages are correctly displayed.
For that I need to check a specific checkbox (or groups of checkboxes) from a list.
Is there a way to specify which checkboxes we want?
Maybe using some kind of parameter that we can pass to choose which we want to select?
Thanks

I figure out how to solve it. I used a collection to identify the elements.
Thanks all for your help!
//products.js
export default create({
main: {
scope: '#main',
allProducts: collection({
itemScope: '.products-list',
item: {
name: text('.card-h1'),
click: clickable('.card-h1'),
color: text('.product-color'),
quantity: text('.product-quantity'),
},
}),
}
});
// products-test.js
function getSingleProduct(name) {
return products.main.allProducts()
.filter(p => p.name.trim() === name).get(0);
}
assert.equal(product.color, 'red');
assert.equal(product.quantity, 10);

Related

Is it possible to pass multiple arguments to onChange action in Ember Power Select?

I'm currently using the excellent ember-power-select add on as part of an ember-bootstrap form.
I have multiple drop down items on the form and I am trying to unify how they are handled into a single function that can be used as the onChange action in the power-select invocations:
{{#form.element
controlType="power-select"
label="Destination"
value=destinationSelection
options=destinationOptions
as |el|}}
{{#el.control
onChange=(action "setDropDown")
searchField="name"
as |item|}}
{{item.name}}
{{/el.control}}
{{/form.element}}
My handler function will simply set some values based on the selection of the drop down:
actions: {
setDropDown(selected, string) {
handleDropDown(selected, dropdown, this)
}
}
function handleDropDown(selected, dropdown, controller) {
let selection = `${dropdown}Selection`
let modelid = `model.${dropdown}_id`
set(controller, selection, selected)
set(controller, modelid, selected.id)
}
In order for this to work I really need to be able to pass a string to the setDropDown action from the onChange part of the component call, otherwise I have no way of telling the handler function which particular fields it should be setting without creating an action per dropdown.
However when I try passing in multiple arguments like
onChange=(action "setDropDown" "destination")
or
onChange=(action "setDropDown" selected "destination")
I lose the basic functionality of the onChange action taking the selected item as it's first argument.
I looked through the documentation and couldn't find any examples where the library author is passing multiple arguments into the onChange action and wondered if it was possible without breaking the functionality of the library.
You can use a specialized higher order helper function to create an action for ember-power-select that will ultimately invoke your action with extra arguments. Consider this helper handle-dropdown
import { helper } from '#ember/component/helper';
export function invokeFunction([prop, action]) {
return function(){
action(prop, ...arguments);
}
}
export default helper(invokeFunction);
So what we are doing here is creating the function that will be invoked by ember-power-select. In this function, we are invoking the original action with prop first, followed by every argument that ember-power-select invoked our onchange function with.
In your template, invoke this helper when passing your action to power-select
{{#power-select
onchange=(handle-dropdown 'foo' (action 'dropdownChanged'))
as |dropdown|}}
And then your action would be
actions: {
dropdownChanged(keyToSet, selectedValue){
this.set(keyToSet, selectedValue);
}
}
This would ultimately call dropdownChanged('foo', /* the selected value */)
Ember Bootstrap's Power Select integration gives you a nice API for use cases like this one. Let me give you an example.
Lets take a country selector as an example. We have a list of countries represented by a list of objects holding their two-letters country code as defined by ISO 3166-1 as id property and their name as name. The selected country should be represented on the model which is a POJO by there country code.
export default Component.extend({
// country code of country selected or null
selectedCountry: null,
// Using a computed property here to ensure that the array
// isn't shared among different instances of the compontent.
// This isn't needed anymore if using native classes and
// class fields.
countries: computed(() => {
return [
{ id: 'us', name: 'United States of America' },
{ id: 'ca', name: 'Canada' },
];
}),
// Using a computed property with getter and setter to map
// country code to an object in countries array.
selectedCountryObject: computed('selectedCountry', {
get() {
return this.countries.find((_) => _.id === this.selectedCountry);
},
set(key, value) {
this.set('selectedCountry', value.id);
return value;
}
}),
});
Now we could use Ember Bootstrap Power Select as expected:
{{#bs-form model=this as |form|}}
{{form.element controlType="power-select" property="selectedCountryObject" label="Country" options=this.countries}}
{{/bs-form}}
Disclaimer: Haven't tested that code myself, so there might be typos but I hope you get the idea.

Simulate a ember-select2 selection in ember integration test

Has anyone been able to simulate a select2 selection of an option?
So far I've tried this:
test("Checking navigation", function () {
expect(1);
visit("/hub");
click("#btnLogin");
andThen(function () {
click(".select2-container");
andThen(function () {
});
});
});
But I have not seen changes in the UI.
We need to hit the anchor inside the container
Ember.$(" .select2-container a").trigger({type:'mousedown', which:1});
If you want to select an item in the dropdown you can do:
Ember.$(".select2-results li div").trigger({type:'mouseup', which:1});
After some playing around, I think this is what you're looking for:
find(".select2-container:first").trigger({type:'mousedown', which:1});

How do I add custom HTML in Rally sdk 2.0?

I'm creating an app with some custom gauges using Rally SDK 2.0. This requires some custom HTML. I took a rake-compiled app.html file from the examples as a starting point. Using JustGage for my gauges. Here is my launch function.
launch: function () {
var info = this.getStoriesForProject(); //Gets some aggregate info
$('#header label').html(info.Title);
var g = new JustGage({
id: "devgauge",
value: info.DevPercent,
levelColors: ['#f80404', '#f8f804', '#50ed0e'],
min: 0,
max: 100,
title: "Dev %"
});
this.add('foo');
},
Then I added some custom HTML in app.html.
Now, if i run this without the code "this.add('foo')", the app adds a new div in the body with class="x-container" and puts my custom HTML outside that div effectively hiding it.
If i use the "this.add('foo') it does NOT create the div class=x-container and it shows my widget just fine.
What is the PROPER way to accomplish what I'm attempting using the 2.0 sdk? I realize the add method is for adding Ext components, but somehow calling this is causing my HTML to render ok. Looking at some apps we developed in the old SDK, using the custom HTML worked just fine in those.
Ext likes to know what is going on layout-wise and often gets confused if you're manually manipulating the dom beneath it without its knowledge. Usually if we have some known set of initial layout we add those via the items collection on the app:
Ext.define('My.App', {
extend: 'Rally.app.App',
items: [
{
xtype: 'container',
itemId: 'header'
},
{
xtype: 'container',
itemId: 'devguage'
}
]
});
Then inside of launch you can add content to those like so:
this.down('#devguage').add({
//some other component
});
You can always just drop all the way down to the element level though as well:
this.down('#header').getEl().dom //the raw html element
By default apps use an auto layout, so any items should flow as you would expect with normal html.
Or, instead of using itemId, you can set the id of the container's element using its id property:
Ext.define('My.App', {
extend: 'Rally.app.App',
items: [
{
xtype: 'container',
id: 'header'
},
{
xtype: 'container',
id: 'devguage'
}
]
});
The resulting html elements will use those ids, which allows you to target them directly with your own custom rendering.

Ember - Clearing an ArrayProxy

On the Ember MVC TodoApp there is an option "Clear all Completed".
I've been trying to do a simple "Clear All".
I've tried multiple things, none of them work as I expected (clearing the data, the local storage and refreshing the UI).
The ones that comes with the sample is this code below:
clearCompleted: function () {
this.filterProperty(
'completed', true
).forEach(this.removeObject, this);
},
My basic test, that I expected to work was this one:
clearAll: function () {
this.forEach(this.removeObject, this);
},
Though, it's leaving some items behind.
If I click the button that calls this function in the Entries controller a couple times the list ends up being empty. I have no clue what's going on! And don't want to do a 'workaround'.
The clearCompleted works perfectly by the way.
The answer depends on what you really want to know-- if you want to clear an ArrayProxy, as per the question title, you just call clear() on the ArrayProxy instance e.g.:
var stuff = ['apple', 'orange', 'banana'];
var ap = Ember.ArrayProxy.create({ content: Ember.A(stuff) });
ap.get('length'); // => 3
ap.clear();
ap.get('length'); // => 0
This way you're not touching the content property directly and any observers are notified (you'll notice on the TodoMVC example that the screen updates if you type Todos.router.entriesController.clear() in the console).
If you're specifically asking about the TodoMVC Ember example you're at the mercy of the quick and dirty "Store" implementation... if you did as above you'll see when you refresh the page the item's return since there is no binding or observing being done between the entry "controller" and the Store (kinda dumb since it's one of Ember's strengths but meh whatev)
Anywho... a "clearAll" method on the entriesController like you were looking for can be done like this:
clearAll: function() {
this.clear();
this.store.findAll().forEach(this.removeObject, this);
}
Well, this worked:
clearAll: function () {
for (var i = this.content.length - 1; i >= 0; i--) {
this.removeObject(this.content[i]);
}
},
If someone can confirm if it's the right way to do it that would be great!

When is the template (.tpl) rendered for an Ext JS Component?

I am trying to inject another component into an element that is rendered by the template of another Coomponent..but in the afterrender event, the template is yet to be rendered so the call to Ext.get(el-id) returns null: TypeError el is null.
tpl:
new Ext.XTemplate(
'<tpl for=".">',
'<ul>',
'<li class="lang" id="cultureSelector-li"></li>',
'</ul>',
'</tpl>'
),
listeners: {
afterrender: {
fn: function (cmp) {
console.log(Ext.get('cultureSelector-li')); // < null :[
Ext.create('CultureSelector', {
renderTo: 'cultureSelector-li'
});
}
}
},
So when can I add this component so that the element is targeting has been created in the DOM?
I think it depends on the component that you are working with. For example, the Data Grid View has a "viewready" event that would suite your needs, and depending what you are attempting, the "boxready" function could work for combo box (only the first render though). Other than that, you can either go up through the element's parent classes searching for the XTemplate render function being called (might be in the layout manager) and extend it to fire an event there, or risk a race condition and just do it in a setTimeout() call with a reasonable delay.
I ended up having to do the work myself. So, I now have the template as a property called theTpl, and then rendered it in beforerender, and then i was able to get a handle on the element in afterrender. This seems wholly counter-intuitive, does anyone have any insight?
beforeRender: {
fn: function (me) {
me.update(me.theTpl.apply({}));
}
},
edit in fact I just extended Component thus:
Ext.define('Ext.ux.TemplatedComponent', {
extend: 'Ext.Component',
alias: 'widget.templatedComponent',
template: undefined,
beforeRender: function () {
var me = this;
var template = new Ext.XTemplate(me.template || '');
me.update(template.apply(me.data || {}));
me.callParent();
}
})
...template accepts an array of html fragments
Turns out I was using the wrong things - apparently we should be using the render* configs for this type of thing (so what are thetpl & data configs for?)
Here's a working fiddle provided for me from the sencha forums:
http://jsfiddle.net/qUudA/10/