How build a composable wrapper that wraps another composable unit with Binding.scala - binding.scala

I am experimenting making composable components using binding.scala. I would like to be able to have a component that can be used to wrap other components that are passed in. For example: a card component that wraps a styled box around any other arbitrary #dom function. I have attempted several approaches but have realized that because of the #dom macro, types seem to be more complicated than they appear.
Below, I have included an approach that doesn't work, but shows the intent. I want to be able to call wrapperMarkup and pass it contentMarkup.
I have found many examples where a data is passed into a #dom function and rendered, but no examples that show how to pass in another #dom function or the results from a #dom call.
Is there a good way to accomplish this?
type MarkupFun = ()=>Binding[Div]
#dom
def contentMarkup():Binding[Div] = {
<div>card Content</div>
}
#dom
def wrapperMarkup(f:MarkupFun):Binding[Div] = {
//<div>card wrapper {f.bind}</div> // What I want that doesn't work
<div>card wrapper {contentMarkup().bind}</div> // works but not what I want.
}

Soon after I posted the question, I found the obvious answer. I was failing to invoke the function before invoking bind.
#dom
def wrapperMarkup(f:MarkupFun):Binding[Div] = {
<div>card wrapper {f().bind}</div>
}
But, any other best practice suggestions would be great.

Related

What is the advantage of Cypress commands over vanilla functions?

It is known that in Cypress we can define custom commands in the commands.js file, with that syntax:
Cypress.Commands.add('login', (email, pw) => {})
Cypress.Commands.overwrite('visit', (orig, url, options) => {})
These commands will then become available across all our tests and can be used from the cy object.
cy.login('my#email.com', '123456')
cy.visit('www.stackoverflow.com', 'www.google.com', { redirect: true })
But I don't get the gist of it. What is the point of doing that when you could just write a regular function?
function login(email, pw) { /* ... */ }
login('my#email.com', '123456')
The only advantage I see is making the function available everywhere without having to export/import it, but you can do that with globals as well. Is that it, or am I missing something?
1. Don't make everything a custom command.
Don't be forget cypress provide API for overwriting existing commands not for only creating new commands.
JS function is great for grouping cy commands and preferable due to it simplicity and readability.
There’s no reason to add this level of complexity when you’re only wrapping a couple of commands only for avoiding Export/Import.
2. Don't overcomplicate things.
A good test automation framework will be is easily understandable, readable and maintainable.Try not to overcomplicate things and create too many abstractions. When in doubt, Always use a JS function.
check out here: https://docs.cypress.io/api/cypress-api/custom-commands#Best-Practices
Yes you are right! The goal is to write cleaner code and availability. With custom commands you can basically write the function in the form of a cypress command and also you don't have to write an import statement in the tests suites. Hence your test suite files remain uniform and cleaner.

Cypress command vs JS function

The Cypress documentation suggests that commands are the right way to reuse fragments of code, e.g.
Cypress.Commands.add("logout", () => {
cy.get("[data-cy=profile-picture]").click();
cy.contains("Logout").click();
});
cy.logout();
For simple cases like this, why would I use a command over a plain JS function (and all the nice IDE assistance that comes with it). What are the drawbacks of rewriting the above snippet as
export function logout(){
cy.get("[data-cy=profile-picture]").click();
cy.contains("Logout").click();
}
// and now somewhere in a test
logout();
Based on my experience with Cypress (one year project and several hundred test cases), I can say that a plan JS function is great for grouping cy commands.
From my point of view, a custom cy command may be really useful only if it is incorporated into the chain processing (utilizes the subject parameter or returns a Chainable to be used further in the chain). Otherwise, a plain JS function is preferable due to it simplicity and full IDE support (unless you're using an additional plugin).
If you for any reason need to do something inside the cypress loop, you can always wrap you code by cy.then() in a plain JS function:
function myFunction() {
cy.then(() => {
console.log(("I'm inside the Cypress event loop"))
})
}
Commands are for behavior that is needed across all tests. For example, cy.setup or cy.login. Otherwise, use functions.
See official docs: https://docs.cypress.io/api/cypress-api/custom-commands#1-Don-t-make-everything-a-custom-command

Is it possible to write these Protractor expectations using no continuations?

Using protractor and jasmine(wd) we want to check that a table on the web page contains expected values. We get fetch the table from the page using a CSS selector:
var table = element(by.css('table#forderungenTable')).all(by.tagName('tr'));
We then set our expectations:
table.then(function(forderungen){
...
forderungen[2].all(by.tagName('td')).then(function(columns){
expect(columns[1].getText()).toEqual('1');
expect(columns[5].getText()).toEqual('CHF 277.00');
});
});
Is it possible to change this code so that we don't have to pass functions to then, in the same way that using jasminewd means that we don't have to do this? See this page, which states:
Protractor uses jasminewd, which wraps around jasmine's expect so that you can write:
expect(el.getText()).toBe('Hello, World!')
Instead of:
el.getText().then(function(text) {
expect(text).toBe('Hello, World!');
});
I know that I could write my own functions in a way similar to which jasminewd does it, but I want know if there is a better way to construct such expectations using constructs already available in protractor or jasminewd.
You can actually call getText() on an ElementArrayFinder:
var texts = element(by.css('table#forderungenTable')).all(by.tagName('tr')).get(2).all(by.tagName('td'));
expect(texts).toEqual(["text1", "text2", "text3"]);

How can I pass a local variable from function to event listener function in JavaScript?

Good day!
I began writing my own basic JavaScript library for personal use and distribution a few days ago, but I am having trouble with one of the methods, specifically bind().
Within the method itself, this refers to the library, the object.
I went to Google and found function.call(), but it didn't work out the way I planned it--it just executed the function.
If you take a look at another method, each(), you'll see that it uses call() to pass values.
I also tried the following:
f.arguments[0]=this;
My console throws an error, saying it cannot read '0' of "undefined".
I would like to be able to pass this (referencing the library--NOT THE WINDOW) to use it in the event listener.
You can see it starting at line 195 of the JavaScript of this JSFiddle.
Here it is as well:
bind:function(e,f){
if(e.indexOf("on")==0){
e=e.replace("on","");
}
if(typeof f==='function'){
/*Right now, 'this' refers to the library
How can I pass the library to the upcoming eventListener?
*/
//f=f(this); doesn't work
//f.call(this); //doesn't work
//this.target refers to the HTMLElement Object itself, which we are adding the eventListener to
//the outcome I'm looking for is something like this:
/*$('h3').which(0).bind(function({
this.css("color:red");
});*/
//(which() defines which H3 element we're dealing with
//bind is to add an event listener
this.target.addEventListener(e,f,false)
}
return this;
},
Thank you so much for your help, contributors!
If, as per your comments, you don't want to use .bind(), rather than directly passing f to addEventListener() you could pass another function that in turn calls f with .call() or .apply():
if(typeof f==='function'){
var _this = this;
this.target.addEventListener(e,function(event){
f.call(_this, event);
},false)
}
Doing it this way also lets your library do any extra event admin, e.g., pre-processing on the event object to normalise properties that are different for different browsers.
So in this particular case you actually want to call JavaScript's built in bind method that all functions have.
f = f.bind(this);
f will be a new function with it's this argument set to whatever you passed into it.
Replace f=f(this); with f.apply(this);
Look at underscore code, here:
https://github.com/jashkenas/underscore/blob/master/underscore.js#L596

Adding custom code to mootools addEvent

Even though I've been using mootools for a while now, I haven't really gotten into playing with the natives yet. Currently I'm trying to extend events by adding a custom addEvent method beside the original. I did that using the following code(copied from mootools core)
Native.implement([Element, Window, Document], {
addMyEvent:function(){/* code here */}
}
Now the problem is that I can't seem to figure out, how to properly overwrite the existing fireEvent method in a way that I can still call the orignal method after executing my own logic.
I could probably get the desired results with some ugly hacks but I'd prefer learning the elegant way :)
Update: Tried a couple of ugly hacks. None of them worked. Either I don't understand closures or I'm tweaking the wrong place. I tried saving Element.fireEvent to a temporary variable(with and without using closures), which I would then call from the overwritten fireEvent function(overwritten using Native.implement - the same as above). The result is an endless loop with fireEvent calling itself over and over again.
Update 2:
I followed the execution using firebug and it lead me to Native.genericize, which seems to act as a kind of proxy for the methods of native classes. So instead of referencing the actual fireEvent method, I referenced the proxy and that caused the infinite loop. Google didn't find any useful documentation about this and I'm a little wary about poking around under the hood when I don't completely understand how it works, so any help is much appreciated.
Update 3 - Original problem solved:
As I replied to Dimitar's comment below, I managed to solve the original problem by myself. I was trying to make a method for adding events that destroy themselves after a certain amount of executions. Although the original problem is solved, my question about extending natives remain.
Here's the finished code:
Native.implement([Element, Window, Document], {
addVolatileEvent:function(type,fn,counter,internal){
if(!counter)
counter=1;
var volatileFn=function(){
fn.run(arguments);
counter-=1;
if(counter<1)
{
this.removeEvent(type,volatileFn);
}
}
this.addEvent(type,volatileFn,internal);
}
});
is the name right? That's the best I could come up with my limited vocabulary.
document.id("clicker").addEvents({
"boobies": function() {
console.info("nipple police");
this.store("boobies", (this.retrieve("boobies")) ? this.retrieve("boobies") + 1 : 1);
if (this.retrieve("boobies") == 5)
this.removeEvents("boobies");
},
"click": function() {
// original function can callback boobies "even"
this.fireEvent("boobies");
// do usual stuff.
}
});
adding a simple event handler that counts the number of iterations it has gone through and then self-destroys.
think of events as simple callbacks under a particular key, some of which are bound to particular events that get fired up.
using element storage is always advisable if possible - it allows you to share data on the same element between different scopes w/o complex punctures or global variables.
Natives should not be modded like so, just do:
Element.implement({
newMethod: function() {
// this being the element
return this;
}
});
document.id("clicker").newMethod();
unless, of course, you need to define something that applies to window or document as well.

Resources