pass function to another and call by name - jquery-plugins

<a id="aHw" href="#" callbackName="helloworld">test</a>
...
<script>
function helloworld() { alert('hello world'); }
</script>
...
question ; how can i produce callBack to pass another function
<script>
...
var cbName = $('#aHw').attr('callbackName');
foo( passfunction ); //How???
...
</script>
<script>
function foo(callBack)
{
callBack(); // call hello world.
}
</script>
thanks in advance.

A function in JavaScript is just an object.
The question(s) don't make terribly much sense to me, but consider the following:
function fn1 () {
alert("fn1")
}
function doIt(cb) {
cb()
}
// direct -- to show point in general
// fn1 evaluates to the function-object, which is then passed
doIt(fn1)
// lookups up fn1 by name, then passes that function-object
// the value "fn1" can be any arbitrary string, such as that which
// was stored in the attr:
// e.g. doIt(window[cbName])
doIt(window["fn1"])
// anon function to iterate sameness
// the anon function returns a new function-object
doIt(function () { alert("anon") })
Happy coding.

Ok. So to have an anchor do something on MouseOver, you'd use this code:
<a id="aHw" href="#" onmouseover="doSomething()">test</a>
You can pass a function to another function this way:
function callSomeFunction( fn )
{
fn();
}
callSomeFunction( alert );
Or you can pass an anonymous function to the above:
callSomeFunction( function(){ alert( "Finally! A message!" ); } );
If you're trying to pass the name of a function as a string (which is a fundamentally bad idea and a terrible risk and hard to debug and DON'T DO IT), then you can use eval:
function callNamedFunction( fn )
{
eval(fn)()
}
Or you might be able to get away with:
function callNamedFunction( fn )
{
(window[fn])()
}

foo( Function('return ' + cbName)() )
I think that's what your after..
But if it's in the browser, and you know that the callback is a global object, you could do..
foo(window[cbName])

Well, if nothing else helps, eval() will:
function foo( callBack ) {
eval( callBack + '()' );
}

If you know where the function is defined (e.g window, or custom namespace) you can invoke it by the string name. Otherwise you would have to eval (bad idea). Also, use data-attributes.
test
...
// invoke runs a function by name in the provided context, or window
function invoke(cbname, context){
return (context || window)[cbname].call();
}
// get attribute through the data method
var funcName = $('a').data('callback');
// run callback
var result = invoke(funcName);

Related

Cypress get attribute value and assign it to variable in function

I am trying to get attribute value and return it from a function.
Here is the code that is working and can be used in a normal test class (into the integration folder).
describe('Example shows how to get attribute value.', () => {
// 'it' is used to create test case. You can add a name of the test case. You can have multiple test cases in one JS class.
it('Get attribute value.', () => {
// Cypress is not able to work with new tabs. It is not possible to switch between tabs. Cypress can manipulate the DOM tree, so we can change the element attributes and open the hyperlink in the same browser tab.
// 'visit()' method is used for navigating to URL address.
cy.visit('https://demoqa.com/links')
cy.xpath('//*[#id="simpleLink"]').then(function (element) {
// 'prop()' method is used to get the attribute value.
const url = element.prop('href')
cy.visit(url)
})
// Assert URL.
cy.url().should('include', 'demoqa.com').should('eq', 'https://demoqa.com/');
})
If I use the code that way - everything is working as expected.
But if I want to re-use the code and create a function like this:
// Give a value of the variable to use it for next function.
functionName = 'addAttribute';
// Declare a Cypress child custom command.
Cypress.Commands.add(functionName, { prevSubject: 'element' }, (subject: any, attributeName: string, attributeValue: string) => {
// Create a try-catch statement. If the function fails - we will recieve the error message.
try {
// Create the function steps after this comment.
cy
.wrap(subject)
.invoke('attr', attributeName, attributeValue)
.should('have.attr', attributeName, attributeValue)
} catch (error) {
// Create the error log and show it to the UI. Show the function name, the class where the function is located and catched error.
let errorMessage = `----------ERROR! It seems that we have an error. Please review the "${functionName}" function from "${__filename.split(__dirname + "/").pop()}" . The error is: ${error}`;
cy.log(errorMessage);
console.log(errorMessage);
}
})
The result is 'object' and I am not sure how to process it.
Here is the rest of the code:
describe("'getAttribute' custom child command example.", () => {
it("example shows how to use 'getAttribute' custom child command.", () => {
cy.visit('https://demoqa.com/buttons');
let attributeValue = cy.element('xpath','(//*[contains(text(),"Click Me")])[3]').getAttribute('class');
cy.log(`The attribute values is: ${attributeValue}`)
});
});
You have to do as below to have a return value:
let attributeValue = '';
cy.element('xpath', '(//*[contains(text(),"Click Me")])[3]')
.getAttribute('class')
.then((attr) => {
attributeValue = attr;
});
cy.log('The attribute values is:' + attributeValue)

How spy works with Jasmine in Angular 7?

I have created this spy using spyOn
it("spyon ", () => {
const searchChangeEmitSpy = spyOn(Adders.countlist,"add");
expect(searchChangeEmitSpy.calls.count()).toEqual(2);
});
and inside Adder class I have the following function
countlist(){ const i =0;
this.quoteList.forEach(element => {
console.log(element);
this.add(4,i++);
});
}
length of quoteList array is 2
what I am getting as a result
Error: : add() method does not exist
I don't think you can directly spy on the function of the class Adders like this, instead spy on the prototype or create an instance of the class and spy on that. I would use two spies and implement it like this:
it("spyon", () => {
const countlistSpy = spyOn(Adders.prototype, 'countlist');
const addSpy = spyOn(Adders.prototype, 'add');
// call your function / trigger something that calls the function
expect(countlistSpy).toHaveBeenCalledTimes(1);
// more expectations here
});
Or with an instance of the class in the beforeEach block you can define your instance like this:
let adder: Adders = new Adders();
And then your test would look like this:
it("spyon", () => {
const countlistSpy = spyOn(adder, 'countlist');
const addSpy = spyOn(adder, 'add');
// call your function / trigger something that calls the function
expect(countlistSpy).toHaveBeenCalledTimes(1);
// more expectations here
});
With the help of Fabian answer, I able to debug and solve my problem. Actually, I need to trigger the function inside the class on which I was spying. after doing so, it gave me the expected output.
test case
it("spyOn countList add()", () => {
const searchChangeEmitSpy = spyOn(Adders,"add");
Adders.addNewQuote("This is my second post");
Adders.countlist(0);
expect(searchChangeEmitSpy.calls.count()).toEqual(2);
});
function inside the class to be spied
countlist(i:number){
this.quoteList.forEach(element => {
console.log(element);
this.add(4,i++);
});
//return i;
}

Passing text from HTML as variable using protractor

I have a table with unknown values (which I need to log in later) and I would like to save one of it with class currentWeek to a variable. HTML is as following:
<tr _ngcontent-c21="" class="currentWeek">
<th _ngcontent-c21="" class="text-center">Value1Foo</th>
(...)
</tr>
In proctracor I created in Helper.ts:
static getfooOfTheWeek() {
let child = element(by.css('.currentWeek')).$('.text-center');
describe('Get foo', function () {
it('get foo', function () {
browser.driver.get('tablepage');
browser.sleep(3000);
return ((child).getText()).toString();
})
})
}
and in file maintest.ts:
describe('Get FOO', function () {
var FOO=Helper.getfooOfTheWeek();
browser.sleep(2000);
//use the value set in Helper.getfooOfTheWeek();
NegativeTest.SomeTest(FOO);
});
but it fails ususally with - Failed: each key must be a number of string; got undefined - therefore I think the FOO is save as an Object, not as string.
I thought also using JSON (to use JSON.parse()), but developers can't gives the values to the table from JSON
Any path what I can try?
Got it - found this issue about saving as string instead of object: Protractor: element.getText() returns an object and not String where I need to resolve the promise
and instead of passing it as variable with var FOO=Helper.getfooOfTheWeek(); I saved it as browser.params.Foo
static getfooOfTheWeek() {
let child = element(by.css('.currentWeek')).$('.text-center');
browser.driver.get('tablepage');
browser.sleep(3000);
child.getText().then(function(text) {
browser.params.Foo=text;
});
}
and I can run NegativeTest.SomeTest(); where the variable is passed in SomeTest() as browser.params.Foo (instead of writting there FOO)

How to assign a back end data value to a string variable in AngularJS?

I was trying to define one sort of global variable which value will be reflecting in 2/3 different templates (directives). For that I used angular factory as follows:
app.factory('MyService', function ($http) {
return {
firstNumber: function (){
//return selectedNumber = "200";
var selectedNumber = "";
var selectedNumber = $http.get("/count.do").success(function (data) {
console.log('First Number: ', data[0].count)
});
return selectedNumber;
}
};
});
As you can see 'selectedNumber' is that common variable. Problem is when I am hard coding the value as "200" and from controller calling as follows:
//Init Number
$scope.selectedNumber= MyService.firstNumber();
This whole process is working fine. But as soon as I am trying to get the value from back end (which you can see above) getting {} object.
I did some research on this and understanding that my concept on Angular object and String manipulation is not clear...can anyone please help me to understand the mistake I am doing and to resolve this situation.
Well, i got my expected outcome by using 'callback' service as follows:
In my factory i just called the '$http.get':
app.factory('MyService', function ($http) {
return {
firstNumber: function (){
$http.get("/count.do").success(callback);
}
};
});
And then from controller i received the data and assigned as follows:
//Init Number
MyService.firstNumber(function(data) {
$scope.selectedNumber = data[0].count;
});
I don't know whether it is a good solution or what, will really appreciate for any comment on this solution plz.
Thanks

jQuery plugins, scope, and binding a created element to the 'change' event

So, I'm writing my first plugin. I'm using the advice on the jQuery website, so my plugin setup looks something like this:
(function( $ ){
var methods = {
init : function( options ) {
var $this = $(this);
// do some code.
// Add an element
$this.append('<select id="test"><option value="yes">Yes</option>' +
'<option value="no">No</option></select>');
// Bind the change handler (chose '.on' after reading the documentation for '.delegate')
$this.on('change', '#test', methods['handler'].call($this, $('#test').val()));
},
handler : function( content ) {
alert ('You chose: ' + content);
}
};
$.fn.testbed = function( method ) {
// Method calling logic
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist' );
}
};
})( jQuery );
I know that the handler itself is working, because I can substitute
function(){ alert ("You did it!");}
for the function call, and it works.
But, the way I'm calling the function now doesn't work. It's how I call other functions from within other methods, but it doesn't work with a handler.
So, my questions are: (1) How do I make it call the function? and (2) is this the best place to set up the handler?
An id should be unique in the page. Having several elements with the same id will give you a different element from what you think when you try to access it. Don't use an id at all, use this in the callback to get the right element.
There is no reason to create a delegate when you are creating one for each event. Bind the event on the select element directly.
The handler for an event should be a function, not the result of a function call.
init : function( options ) {
// Add an element
this.append('<select><option value="yes">Yes</option>' +
'<option value="no">No</option></select>');
// Bind the change handler
$('select', this).on('change', function() {
methods['handler']($(this).val());
});
},

Resources