I have build a jQuery widget for Footer bar. This bar contain some click-able event. I want to write unit test to verify the functionality. For testing I am using qunit. I want to create test unit for these functionality:-
Check bar is loaded
Check on close button bar should be minimized
On next click on close button bar again get maximized
Can someone help me to write correct test modules.
Here is my footer bar widget code :-
// widget for footer bar
(function($){
$.widget("ui.footerbar", {
options: {
id: null, //id for the DOM element
offset:0, // relative to right edge of the browser window
width: '100%', // width of the chatbox
boxClosed: function(id) {}, // called when the close icon is clicked
},
init: function(elem) {
this.elem = elem;
},
widget: function() {
return this.uiFooterbar
},
_create: function(){
var self = this,
options = self.options,
offset = options.offset,
title = options.title || "No Title",
// footerbar
uiFooterbar = (self.uiFooterbar = $('<div></div>'))
.appendTo(document.body)
.attr('id', 'stickybar'),
// close button tab
uiFooterbarClosebox = (self.uiFooterbarClosebox = $('<div></div>'))
.addClass('vmchat_bar_button'
)
.attr('id', 'hide_bar')
.appendTo(uiFooterbar),
uiFooterbarClose = (self.uiFooterbarClose = $('<input>'))
.attr('id', 'closebtn')
.attr('type', 'button')
.appendTo(uiFooterbarClosebox)
.toggle(function () { alert('click1');
$('#stickybar').effect("size", { to: {width: 36}, origin: ['bottom','right'] }, 1000, function (){$('#stickybar').css("left", "97%")});
}, function () {alert('click2');
$('#stickybar').effect("size", { to: {width: self.options.width}, origin: ['bottom','left'] }, 100, function (){$('#stickybar').css("left", 0)});
});
//chatroom tab
uiFooterbarchatroomtab = (self.uiFooterbarchatroomtab = $('<div></div>'))
.addClass('vmchat_bar_button'
)
.attr('id', 'chatroom_bt')
.appendTo(uiFooterbar),
uiFooterbarchatroomContent = (self.uiFooterbarchatroomContent = $('<div class="inner_bt"></div>'))
.appendTo(uiFooterbarchatroomtab)
uiFooterbarchatroomIcon= (self.uiFooterbarchatroomIcon = $('<div id="chatroom_icon"></div>'))
.appendTo(uiFooterbarchatroomContent)
uiFooterbarchatroomText= (self.uiFooterbarchatroomText = $('<div id="chatroom_text"></div>'))
.appendTo(uiFooterbarchatroomContent)
.text('Chatroom')
.click(function(){
alert('open comman chat room');
})
//userlist tab
uiFooterbarUserlisttab = (self.uiFooterbarUserlisttab = $('<div></div>'))
.addClass('vmchat_bar_button'
)
.attr('id', 'user_list')
.appendTo(uiFooterbar),
uiFooterbarUserlistContent = (self.uiFooterbarUserlistContent = $('<div class="inner_bt"></div>'))
.appendTo(uiFooterbarUserlisttab)
uiFooterbarUserlistIcon= (self.uiFooterbarUserlistIcon = $('<div id="usertab_icon"></div>'))
.appendTo(uiFooterbarUserlistContent)
uiFooterbarUserlistText= (self.uiFooterbarUserlistText = $('<div id="usertab_text"></div>'))
.appendTo(uiFooterbarUserlistContent)
.text('Private Chat')
.click(function(){
alert('open comman chat room');
})
self._setWidth(self.options.width);
self.init(self);
},
destroy: function () {
this.element.remove();
// if using jQuery UI 1.8.x
$.Widget.prototype.destroy.call(this);
// if using jQuery UI 1.9.x
//this._destroy();
},
_setWidth: function(width) {
this.uiFooterbar.width(width + "px");
}
});
}(jQuery));
For testing I have created these modules:-
Testing bar is loaded and visible
var el;
var body = document.body;
function bar(){
return el.footerbar("widget");
}
(function($){
module("core");
test("init", function(){
el = $("#qunit-fixture").footerbar();
ok( bar().is(':visible'), 'bar is open');
});
})(jQuery);
Testing correct Number of tabs
(function($){
var el, widget, elems;
module("html", {
setup: function() {
el = $("#qunit-fixture").footerbar();
widget = el.footerbar("widget");
}
});
test("check close button", function(){
expect(4);
elems = widget.find('.vmchat_bar_button');
equal( elems.length, 3, 'There are three Tabs' );
equal(widget.find('input[id="closebtn"]').parents('div').attr("id"),'hide_bar','close button is present');
equal(widget.find('div[id="chatroom_text"]').parent().hasClass('inner_bt'),true,'chatroom tab is present');
equal(widget.find('div[id="usertab_text"]').parent().hasClass('inner_bt'),true,'user list tab is present');
});
})(jQuery);
Testing bar get minimize/maximize on close button click
(function($){
module("event");
test("footerbaropen", function(){
// inject widget
el = $("#qunit-fixture").footerbar();
el.footerbar({})
equal(bar().css('left'),'0px' ,'bar is open');
// fire click event on close button
bar().find("#closebtn").trigger("click");
equal(bar().css('left'),'97%' ,'bar is closed'); // this is not working
});
})(jQuery);
Top two test seems to be working fine but in third test when click event is triggered bar does not get minimized in this block. Its status get changed when it exit out.
What should I do to get changed status of footer bar, if it is minimized or if it is active?
Problem is $('#stickybar').effect.
Your test is running before your footer bar complete this effect.
Related
I have a sortable list in React which is powered by jQuery UI. When I drag and drop an item in the list, I want to update the array so that the new order of the list is stored there. Then re-render the page with the updated array. i.e. this.setState({data: _todoList});
Currently, when you drag and drop an item, jQuery UI DnD works, but the position of the item in the UI does not change, even though the page re-renders with the updated array. i.e. in the UI, the item reverts to where it used to be in the list, even though the array that defines its placement has updated successfully.
If you drag and drop the item twice, then it moves to the correct position.
// Enable jQuery UI Sortable functionality
$(function() {
$('.bank-entries').sortable({
axis: "y",
containment: "parent",
tolerance: "pointer",
revert: 150,
start: function (event, ui) {
ui.item.indexAtStart = ui.item.index();
},
stop: function (event, ui) {
var data = {
indexStart: ui.item.indexAtStart,
indexStop: ui.item.index(),
accountType: "bank"
};
AppActions.sortIndexes(data);
},
});
});
// This is the array that holds the positions of the list items
var _todoItems = {bank: []};
var AppStore = assign({}, EventEmitter.prototype, {
getTodoItems: function() {
return _todoItems;
},
emitChange: function(change) {
this.emit(change);
},
addChangeListener: function(callback) {
this.on(AppConstants.CHANGE_EVENT, callback);
},
sortTodo: function(todo) {
// Dynamically choose which Account to target
targetClass = '.' + todo.accountType + '-entries';
// Define the account type
var accountType = todo.accountType;
// Loop through the list in the UI and update the arrayIndexes
// of items that have been dragged and dropped to a new location
// newIndex is 0-based, but arrayIndex isn't, hence the crazy math
$(targetClass).children('form').each(function(newIndex) {
var arrayIndex = Number($(this).attr('data-array-index'));
if (newIndex + 1 !== arrayIndex) {
// Update the arrayIndex of the element
_todoItems[accountType][arrayIndex-1].accountData.arrayIndex = newIndex + 1;
}
});
// Sort the array so that updated array items move to their correct positions
_todoItems[accountType].sort(function(a, b){
if (a.accountData.arrayIndex > b.accountData.arrayIndex) {
return 1;
}
if (a.accountData.arrayIndex < b.accountData.arrayIndex) {
return -1;
}
// a must be equal to b
return 0;
});
// Fire an event that re-renders the UI with the new array
AppStore.emitChange(AppConstants.CHANGE_EVENT);
},
}
function getAccounts() {
return { data: AppStore.getTodoItems() }
}
var Account = React.createClass({
getInitialState: function(){
return getAccounts();
},
componentWillMount: function(){
AppStore.addChangeListener(this._onChange);
// Fires action that triggers the initial load
AppActions.loadComponentData();
},
_onChange: function() {
console.log('change event fired');
this.setState(getAccounts());
},
render: function(){
return (
<div className="component-wrapper">
<Bank data={this.state.data} />
</div>
)
}
});
The trick is to call sortable('cancel') in the stop event of the Sortable, then let React update the DOM.
componentDidMount() {
this.domItems = jQuery(React.findDOMNode(this.refs["items"]))
this.domItems.sortable({
stop: (event, ui) => {
// get the array of new index (http://api.jqueryui.com/sortable/#method-toArray)
const reorderedIndexes = this.domItems.sortable('toArray', {attribute: 'data-sortable'})
// cancel the sort so the DOM is untouched
this.domItems.sortable('cancel')
// Update the store and let React update (here, using Flux)
Actions.updateItems(Immutable.List(reorderedIndexes.map( idx => this.state.items.get(Number(idx)))))
}
})
}
The reason jQuery UI Sortable doesn't work with React is because it directly mutates the DOM, which is a big no no in React.
To make it work, you would have to modify jQuery UI Sortable so that you keep the DnD functionality, but when you drop the element, it does not modify the DOM. Instead, it could fire an event which triggers a React render with the new position of the elements.
Since React uses a Virtual DOM, you have to use the function React.findDOMNode() to access an actual DOM element.
I would call the jQuery UI function inside the componentDidMount method of your component because your element has to be already rendered to be accessible.
// You have to add a ref attribute to the element with the '.bank-entries' class
$( React.findDOMNode( this.refs.bank_entries_ref ) ).sortable( /.../ );
Documentation - Working with the browser (everything you need to know is here)
Hope that makes sense and resolves your issue
I'm new to jQuery UI.
I'm trying to create a selectable jQuery UI tooltip. The tooltip is associated with the links on a page.
When the link is surrounded by just text, it works fine. But when there are few links next to each other, the functionality overlaps and tooltips don't show smoothly anymore.
you can find the code on http://jsfiddle.net/zumot/Hc3FK/2/
Below the JavaScript code
$("[title][data-scan]").bind("mouseleave", function (event) {
event.stopImmediatePropagation();
var fixed = setTimeout('$("[title][data-scan]").tooltip("close")', 100);
$(".ui-tooltip").click(function () {
alert("I am clickable");
return false;
});
$(".ui-tooltip").hover(
function () {
clearTimeout(fixed);
},
function () {
$("[title][data-scan]").tooltip("close");
});}).tooltip({
items: "img, [data-scan], [title]",
content: function () {
var element = $(this);
if (element.is("[data-scan]")) {
var text = element.attr("href");
return "<a href='http://www.google.com'>You are trying to open a tooltip <span>" + text + "</span></a>";
}
if (element.is("[title]")) {
return element.attr("title");
}
if (element.is("img")) {
return element.attr("alt");
}
},
position: {
my: "right center",
at: "left center",
delay: 200,
using: function (position, feedback) {
$(this).css(position);
$("<div>")
.addClass(feedback.vertical)
.addClass(feedback.horizontal)
.appendTo(this);
}
}});
My attempt to fix the issue was by making the variable fixed global (to make it accessible by other jQuery UI properties), and on Open event, hide any other previously opened tooltips and clear the timeout id saved in fixed variable.
You can find the solution here http://jsfiddle.net/zumot/dVGWB/
, though to see the code working properly, you'll have to run it directly on your browser.
Here's the snapshort of the fixed code.
// Make the timeout id variable global
var fixed = 0;
$("[title][data-scan]").tooltip({
items: "img, [data-scan], [title]",
content: function () {
var element = $(this);
if (element.is("[data-scan]")) {
var text = element.attr("href");
return "<a href='http://www.google.com'>You are trying to open a tooltip <span>" + text + "</span></a>";
}
if (element.is("[title]")) {
return element.attr("title");
}
if (element.is("img")) {
return element.attr("alt");
}
},
open: function (event, ui) {
// When opening a new div, hide any previously opened tooltips first.
$(".ui-tooltip:not([id=" + ui.tooltip[0].id + "])").hide();
// clear timeout as well if there's any.
if (tf > 0) {
clearTimeout(tf)
};
},
position: {
my: "right center",
at: "left center",
delay: 200,
using: function (position, feedback) {
$(this).css(position);
$("<div>")
.addClass(feedback.vertical)
.addClass(feedback.horizontal)
.appendTo(this);
}
}
}).bind("mouseleave", function (event) {
// stop defeulat behaviour
event.stopImmediatePropagation();
fixed = setTimeout('$("[title][data-scan]").tooltip("close")', 100);
$(".ui-tooltip").hover(
function () {
clearTimeout(tf);
}, function () {
$("[title][data-scan]").tooltip("close");
})
});
I load pages created from templates dynamically with a function from the Router (as seen on some tutorials):
changePage: function(page) { // page is a View object
$(page.el).attr('data-role', 'page');
page.render();
$('body').append($(page.el));
var transition = $.mobile.defaultPageTransition;
if (this.firstPage) {
transition = 'none';
this.firstPage = false;
}
$.mobile.changePage($(page.el), {changeHash:false, transition: transition});
}
The thing is when pages contain a JQ Mobile navbar, the active item is not highlighted. Actually it is, like 1 ms, then it's not, I feel like it's because the navbar is "reloaded".
When I click 2 times on the same item, it works the second time.
Is there anybody who is able to have working navbars with jQuery Mobile and backbone.js?
I ended up doing that:
var activeTab = null;
$('[data-role=page]').live('pageshow', function (event, ui) {
$.each($('[data-role=navbar] ul li').children(), function (i, val) {
if (typeof activeTab !== "undefined" && activeTab != null && $(val).attr('id') == 'navTab' + activeTab)
$(val).addClass($.mobile.activeBtnClass);
else
$(val).removeClass($.mobile.activeBtnClass);
});
activeTab = null;
});
And for each route that requires an active tab I just do for instance:
r_search: function() { // Search page (form)
activeTab = "Search";
this.changePage(new SearchView());
},
I'm a begginer jQuery dev, and I'm using the jQuery UI dialog to show up properties of an object (whatever).
var $dialog = $('<div></div>')
.html('This dialog will show every time!')
.dialog({
autoOpen: false,
title: 'Properties'
});
So this is the dialog, and let's say I have a FOR structure in which I add properties (p tags) and for some of those properties I want a button.
var dialogHtml = "";
var dialog_buttons = {};
for (var key in d.properties){
var str;
var str = '<p>' + something + '</p>';
if (condition){
dialog_buttons[key] = function()
{ functionName(key); };
}
dialogHtml = dialogHtml + str;
}
$dialog.dialog( "option", "buttons", dialog_buttons );
$dialog.dialog('open');
And somewhere else I have the function:
function functionName(key){
// something something
}
This is where I have the problem: the key variable that's passed to the function... when the button is called the key is the last value from the iteration.
Let's say we have keys 1, 2, 3, 4 then when the button is clicked, the key parameter will be 4.
I want when I click a button from the dialog, to know which button was pressed.
Can anybody help me?
Thanks!
Using the event data from the click event (see here)
$('#btn').click(function(e) {
functionName(e.target);
});
Explanation of Event.Target
By default the jQuery U Autocomplete produces a list of results, upon clicking on a result it will populate the text field with the clicked result text.
I would like to change this behaviour, so that when clicking on a result it will take you to that result's page. To generate the hyperlink I can pass in the ID of the result.
I'm using PHP JSON to bring back the resultset:
$return_arr = array();
while ($row = mysql_fetch_array($fetch, MYSQL_ASSOC)) {
$row_array['id'] = $row['id'];
$row_array['value'] = $row['name'];
array_push($return_arr, $row_array);
}
echo json_encode($return_arr);
And here is my current jQuery:
$(function() {
$("#searchcompany").autocomplete( {
source: "companies.php",
minLength: 2
});
});
Think you need to hook into the select event and supply your own function.
See here for more information.
Supply a callback function to handle the select event as an init option.
$("#searchcompany").autocomplete( {
source: "companies.php",
minLength: 2,
select: function(event,ui) { //Do your code here...
event.preventDefault();
}
});
or Bind to the select event by type: autocompleteselect.
$( "#searchcompany" ).bind( "autocompleteselect", function(event, ui) {
...
});
and to change the matching items to include a hyperlink that can be clicked use the Open event :-
open: function(event, ui) { $( 'li.ui-menu-item a').each( function() {
var el = $(this);
el.attr('href', el.html());
}
); }
This will add an href="[item value]" to each <a> element.
Edit: The code below will allow you to use the open event to change the items to include a href so they show the link in the window and when clicked they will take you to the specified location :-
open: function(event, ui) {
$("ul.ui-autocomplete").unbind("click");
var data = $(this).data("autocomplete");
for(var i=0; i<=data.options.source.length-1;i++)
{
var s = data.options.source[i];
$("li.ui-menu-item a:contains(" + s.value + ")").attr("href", "directory/listing/" + s.id);
}
}
Using this also means that you don't need to use the select event.