My view contains a IconTabBar with various IconTabFilters.
How can I switch the currently active tab to another one? The usual trigger("tap") approach does not seem to work.
After some debugging I figured out that pseudo event saptouchend triggered on the icon within the IconTabFilter works.
return this.waitFor({
id: "mySecondTab-icon",
viewName: sViewName,
success: function (control) {
control.$().trigger("saptouchend");
},
errorMessage: "Second IconTabFilter not found in IconTabBar"
});
I like your solution
Normally I look at the QUnit tests for the control and hope they have done something similar
eg
// Press SPACE key on second IconTabFilter to expand
sap.ui.test.qunit.triggerKeydown(oControl.$(), jQuery.sap.KeyCodes.SPACE);
UPDATE:
just noticed a solution in the TEST Tutorial - not sure i like it though as it doesn't simulate user interaction
iPressOnTheTabWithTheKey: function (sKey) {
return this.waitFor({
id: "iconTabBar",
viewName : sViewName,
success: function (oIconTabBar) {
oIconTabBar.setSelectedKey(sKey);
},
errorMessage: "Cannot find the icon tab bar"
});
}
An easy way to do that can we to use the id/icon to pick the iconTab which you want and then add a action to it, which in your case will be a press.
iClickOnSecondIconTabBAr: function() {
return this.waitFor({
controlType: "sap.m.IconTabFilter",
matchers: new sap.ui.test.matchers.Properties({
icon: "sap-icon://inspection"
}),
actions: new Press(),
errorMessage: "Did not find the IconTabBar Button!"
});
}
Related
Below a typical action to test if a sap.m.Select contains an item with the name xyz and then select it.
success: function(oSelect) {
var oItems = oSelect.getItems();
$.each(oItems, function(i,v) {
if(oItems[i].getText() === "TestItemNameILikeToSelect") {
oTestOpa5TestItem = oItems[i];
}
});
if(oTestOpa5TestItem !== null) {
oSelect.setSelectedKey(oTestOpa5TestItem.getKey());
oTestOpa5TestItem.$().trigger("tap");
}
},
When I start the test run it does correctly select the proper item from the list and sets it visibly in the browser, but it does not trigger the attached event that is behind (e.g. change="onListItemChanged").
My application works fine, but I don't find a way to create a working test for it.
Thanks in advance
OPA5 has an 'Action' interface and two default implementations e.g. 'EnterText' and 'Press'. The recommended usage is to define an action block on the waitFor() options like this:
When.waitFor({
id: "myButton",
actions: new Press()
});
What you use is the 'old way' but it has some shortcomings:
success block is not synchronized with XHR requests but action is.
Sending a click/tap event to a control could require selecting some internal element. Imagine a click to nav container - there are several places you could click actually. Actions handle those details and define a standard behavior you could depend on.
It is better to encapsulate your selection logic inside a matchers block and even abstract it to a custom matcher. This way your success block will be cleaner and you could reuse the matcher in several places in your test.
OPA5 Actions
Have a look at the official UI5 Demo Kit, under samples > OPA5 > Code: Simulating user interactions on UI5 controls with OPA5, You will be able to find numerous examples of OPA 5 testing regarding general user interactions. In your case for the select:
opaTest("Should select an item in a sap.m.Select", function(Given, When, Then) {
When.waitFor({
id: "mySelect",
actions: new Press(),
success: function(oSelect) {
this.waitFor({
controlType: "sap.ui.core.Item",
matchers: [
new Ancestor(oSelect),
new Properties({ key: "Germany"})
],
actions: new Press(),
success: function() {
Opa5.assert.strictEqual(oSelect.getSelectedKey(), "Germany", "Selected Germany");
},
errorMessage: "Cannot select Germany from mySelect"
});
},
errorMessage: "Could not find mySelect"
});
});
https://sapui5.hana.ondemand.com/#/entity/sap.ui.test.Opa5/sample/sap.ui.core.sample.OpaAction/code/Opa.js
My view contains a sap.m.SearchField.
How can I invoke the search in that field? The usual trigger("tap") approach does not seem to work.
After some debugging I found out that a combination of the pseudo events saptouchstart and saptouchend triggered in the magnifying glass icon within the search field works.
return this.waitFor({
id: "mySearchField",
viewName: sViewName,
success: function (control) {
var event, searchIcon;
event = jQuery.Event( "saptouchend" );
event.originalEvent = event; // would otherwise cause NPE at some point in SAP code
searchIcon = control.$().find("div[id*=mySearchField-search]");
searchIcon.trigger("saptouchstart").trigger(event);
},
errorMessage: "Search field not found"
});
This can also be done with the following one-liner (source):
$(theSearchField).trigger("onSearch");
But maybe this is only possible with a more recent version of UI5 than the one used by you.
Another solution approach would be to use an EnterText action, which not only enters the search term ("foobar" in the following example), but also triggers the search afterwards:
this.waitFor({
id: "mySearchField",
actions: [ new sap.ui.test.actions.EnterText({ text: "foobar" }) ]
});
I am using pnotify and loading callback function to show a notification when the fullcalendar plugin has loaded all events.
loading:function(isLoading, view){
if (isLoading === false){
new PNotify({
title:"Finished loading events",
type:'success',
delay: 1000
});
My problems is that when ever I move to different dates it calls loading again so I am left with so many notifications shown on my screen that it becomes very unusable. How can I bypass this? Is there a way to check if a notification is active and just change the text and title of it?
You can add that logic based on the template you're using (check the template docs).
Your code would be something like
loading:function(isLoading, view){
var exists = false;
$(".ui-pnotify-title").each(function() {
if ($(this).html() == 'Finished loading events')
exists = true;
});
if (!exists) {
new PNotify({
title:"Finished loading events",
type:'success',
delay: 1000
});
}
}
It would be better if you could use a specific id or class to detect if the notification is already shown, but this works.
Take a look at the working jsfiddle.
You can just store it in a variable, do your necessary code (like nullable/undefined checks, etc) and call "update()" (here: http://sciactive.com/pnotify/ - for example, find for 'Click Notice' and see the source)
var p = new PNotify({
title: 'Some title',
text: 'Check me out! I\'m a error.',
type: 'error',
icon: 'fa fa-times-circle'
});
// ... code ...
p.update({title: 'My new title'});
I have the following template:
<template name="modalTest">
{{session "modalTestNumber"}} <button id="modalTestIncrement">Increment</button>
</template>
That session helper simply is a go-between with the Session object. I have that modalTestNumber initialized to 0.
I want this template to be rendered, with all of it's reactivity, into a bootbox modal dialog. I have the following event handler declared for this template:
Template.modalTest.events({
'click #modalTestIncrement': function(e, t) {
console.log('click');
Session.set('modalTestNumber', Session.get('modalTestNumber') + 1);
}
});
Here are all of the things I have tried, and what they result in:
bootbox.dialog({
message: Template.modalTest()
});
This renders the template, which appears more or less like 0 Increment (in a button). However, when I change the Session variable from the console, it doesn't change, and the event handler isn't called when I click the button (the console.log doesn't even happen).
message: Meteor.render(Template.modalTest())
message: Meteor.render(function() { return Template.modalTest(); })
These both do exactly the same thing as the Template call by itself.
message: new Handlebars.SafeString(Template.modalTest())
This just renders the modal body as empty. The modal still pops up though.
message: Meteor.render(new Handlebars.SafeString(Template.modalTest()))
Exactly the same as the Template and pure Meteor.render calls; the template is there, but it has no reactivity or event response.
Is it maybe that I'm using this less packaging of bootstrap rather than a standard package?
How can I get this to render in appropriately reactive Meteor style?
Hacking into Bootbox?
I just tried hacked into the bootbox.js file itself to see if I could take over. I changed things so that at the bootbox.dialog({}) layer I would simply pass the name of the Template I wanted rendered:
// in bootbox.js::exports.dialog
console.log(options.message); // I'm passing the template name now, so this yields 'modalTest'
body.find(".bootbox-body").html(Meteor.render(Template[options.message]));
body.find(".bootbox-body").html(Meteor.render(function() { return Template[options.message](); }));
These two different versions (don't worry they're two different attempts, not at the same time) these both render the template non-reactively, just like they did before.
Will hacking into bootbox make any difference?
Thanks in advance!
I am giving an answer working with the current 0.9.3.1 version of Meteor.
If you want to render a template and keep reactivity, you have to :
Render template in a parent node
Have the parent already in the DOM
So this very short function is the answer to do that :
renderTmp = function (template, data) {
var node = document.createElement("div");
document.body.appendChild(node);
UI.renderWithData(template, data, node);
return node;
};
In your case, you would do :
bootbox.dialog({
message: renderTmp(Template.modalTest)
});
Answer for Meteor 1.0+:
Use Blaze.render or Blaze.renderWithData to render the template into the bootbox dialog after the bootbox dialog has been created.
function openMyDialog(fs){ // this can be tied to an event handler in another template
<! do some stuff here, like setting the data context !>
bootbox.dialog({
title: 'This will populate with content from the "myDialog" template',
message: "<div id='dialogNode'></div>",
buttons: {
do: {
label: "ok",
className: "btn btn-primary",
callback: function() {
<! take some actions !>
}
}
}
});
Blaze.render(Template.myDialog,$("#dialogNode")[0]);
};
This assumes you have a template defined:
<template name="myDialog">
Content for my dialog box
</template>
Template.myDialog is created for every template you're using.
$("#dialogNode")[0] selects the DOM node you setup in
message: "<div id='dialogNode'></div>"
Alternatively you can leave message blank and use $(".bootbox-body") to select the parent node.
As you can imagine, this also allows you to change the message section of a bootbox dialog dynamically.
Using the latest version of Meteor, here is a simple way to render a doc into a bootbox
let box = bootbox.dialog({title:'',message:''});
box.find('.bootbox-body').remove();
Blaze.renderWithData(template,MyCollection.findOne({_id}),box.find(".modal-body")[0]);
If you want the dialog to be reactive use
let box = bootbox.dialog({title:'',message:''});
box.find('.bootbox-body').remove();
Blaze.renderWithData(template,function() {return MyCollection.findOne({_id})},box.find(".modal-body")[0]);
In order to render Meteor templates programmatically while retaining their reactivity you'll want to use Meteor.render(). They address this issue in their docs under templates.
So for your handlers, etc. to work you'd use:
bootbox.dialog({
message: Meteor.render(function() { return Template.modalTest(); })
});
This was a major gotcha for me too!
I see that you were really close with the Meteor.render()'s. Let me know if it still doesn't work.
This works for Meteor 1.1.0.2
Assuming we have a template called changePassword that has two fields named oldPassword and newPassword, here's some code to pop up a dialog box using the template and then get the results.
bootbox.dialog({
title: 'Change Password',
message: '<span/>', // Message can't be empty, but we're going to replace the contents
buttons: {
success: {
label: 'Change',
className: 'btn-primary',
callback: function(event) {
var oldPassword = this.find('input[name=oldPassword]').val();
var newPassword = this.find('input[name=newPassword]').val();
console.log("Change password from " + oldPassword + " to " + newPassword);
return false; // Close the dialog
}
},
'Cancel': {
className: 'btn-default'
}
}
});
// .bootbox-body is the parent of the span, so we can replace the contents
// with our template
// Using UI.renderWithData means we can pass data in to the template too.
UI.insert(UI.renderWithData(Template.changePassword, {
name: "Harry"
}), $('.bootbox-body')[0]);
I am using TextExtJs for an autocomplete feature where you start typing and the dropdown of suggestions appears below the text input and you can select a suggested option with arrow keys or mouse.
Everything is working great except that I am trying to perform a function after the user selects one of the suggestions. There is a hideDropdown event which I think is the proper event to use for this. Unfortunately I'm not understanding how to do this, this is what I have tried:
$('#usearch').textext({
plugins : 'autocomplete ajax',
ajax : {
url : 'usersuggest.php',
dataType : 'json',
cacheResults : true
},
autocomplete : {
onHideDropdown : function(){
alert('A happened');
},
hideDropdown : function(){
alert('B happened');
}
},
onHideDropdown : function(){
alert('C happened');
},
hideDropdown : function(){
alert('D happened');
}
});
None of these functions with the alert actually ever run. They do not interfere with the suggestion piece of it. How do I attach a callback to this event?
I'm facing the same problem here....
Unfortunately there is no proper solution. The manual is as rudimental as the examples provided on the plugin page.
I managed to bind a kind of "onAddingTag" event, refer to this: http://textextjs.com/manual/plugins/tags.html#istagallowed
$('#textarea').textext().bind('isTagAllowed', function(e, data) {
var valueAdded = data.tag;
data.result = true; //needs to be done, since we're abusing this event
};
Despite the fact that this may help with this issue, your next problem would be: when does the user remove a tag?
Finally I ended up, using another autocomplete library.