How to properly create and use dynamic Xpath in JSON (Page Object Model) - Karate DSL - karate

For example, I have this sample JSON object in pages folder which contains all the XPaths for specific page.
{
"pageTitle1": "//*[#class='page-title' and text()='text1']",
"pageTitle2": "//*[#class='page-title' and text()='text2']",
"pageTitle_x" : "//*[#class='page-title' and text()='%s']"
}
* def pageHome = read('classpath:/pages/pageHome.json')
* click(pageHome.pageTitle_x) <-- how to properly replace %s in the string?
Update: I tried the replace function, not sure if this is the proper way.
* click(pageHome.pageTitle_x.replace("%s","new value"))

First a bit of advice. Trying to be "too clever" like this causes maintainability problems in the long run. I have said a lot about this here, please read it: https://stackoverflow.com/a/54126724/143475
That said, you can write re-usable JS functions that will do all these things:
* def pageTitle = function(x){ return "//*[#class='page-title' and text()='" + x "']" }
Now using that you can do this:
* click(pageTitle('foo'))
If you redesign the function even this may be possible:
* click(pageTitle(pageHome.pageTitle_x, 'foo'))
But see how things become more complicated and less readable. The choice is yours. Note that anything you can do in JS (e.g. String.replace()) will be possible, it is up to you and your creativity.

Related

Slick plain sql query with pagination

I have something like this, using Akka, Alpakka + Slick
Slick
.source(
sql"""select #${onlyTheseColumns.mkString(",")} from #${dbSource.table}"""
.as[Map[String, String]]
.withStatementParameters(rsType = ResultSetType.ForwardOnly, rsConcurrency = ResultSetConcurrency.ReadOnly, fetchSize = batchSize)
.transactionally
).map( doSomething )...
I want to update this plain sql query with skipping the first N-th element.
But that is very DB specific.
Is is possible to get the pagination bit generated by Slick? [like for type-safe queries one just do a drop, filter, take?]
ps: I don't have the Schema, so I cannot go the type-safe way, just want all tables as Map, filter, drop etc on them.
ps2: at akka level, the flow.drop works, but it's not optimal/slow, coz it still consumes the rows.
Cheers
Since you are using the plain SQL, you have to provide a workable SQL in code snippet. Plain SQL may not type-safe, but agile.
BTW, the most optimal way is to skip N-th element by Database, such as limit in mysql.
depending on your database engine, you could use something like
val page = 1
val pageSize = 10
val query = sql"""
select #${onlyTheseColumns.mkString(",")}
from #${dbSource.table}
limit #${pageSize + 1}
offset #${pageSize * (page - 1)}
"""
the pageSize+1 part tells you whether the next page exists
I want to update this plain sql query with skipping the first N-th element. But that is very DB specific.
As you're concerned about changing the SQL for different databases, I suggest you abstract away that part of the SQL and decide what to do based on the Slick profile being used.
If you are working with multiple database product, you've probably already abstracted away from any specific profile, perhaps using JdbcProfile. In that case you could place your "skip N elements" helper in a class and use the active slickProfile to decide on the SQL to use. (As an alternative you could of course check via some other means, such as an environment value you set).
In practice that could be something like this:
case class Paginate(profile: slick.jdbc.JdbcProfile) {
// Return the correct LIMIT/OFFSET SQL for the current Slick profile
def page(size: Int, firstRow: Int): String =
if (profile.isInstanceOf[slick.jdbc.H2Profile]) {
s"LIMIT $size OFFSET $firstRow"
} else if (profile.isInstanceOf[slick.jdbc.MySQLProfile]) {
s"LIMIT $firstRow, $size"
} else {
// And so on... or a default
// Danger: I've no idea if the above SQL is correct - it's just placeholder
???
}
}
Which you could use as:
// Import your profile
import slick.jdbc.H2Profile.api._
val paginate = Paginate(slickProfile)
val action: DBIO[Seq[Int]] =
sql""" SELECT cols FROM table #${paginate.page(100, 10)}""".as[Int]
In this way, you get to isolate (and control) RDBMS-specific SQL in one place.
To make the helper more usable, and as slickProfile is implicit, you could instead write:
def page(size: Int, firstRow: Int)(implicit profile: slick.jdbc.JdbcProfile) =
// Logic for deciding on SQL goes here
I feel obliged to comment that using a splice (#$) in plain SQL opens you to SQL injection attacks if any of the values are provided by a user.

Karate Netty - CallSingle but not so single

What I had till today:
I have get_jwt.feature and I call it as a part of karate-config.js. Since I have used one account test#test.com I needed only one jwt and I can reuse it across scenarios. callSingle worked as a charm in this case.
Today:
Suddenly I have need for jwt's from two accounts which I dont want to generate for each scenario, callSingle falls short of this task as it does exactly what its supposed to be doing. Now I have hacky idea, I can simply make two files, get_jwt.feature and get_jwt_user2.feature, and single call them each.
So my question: Is there a better way of doing this?
You can use "2 levels" of calls. So point the callSingle() to a JS function that calls get_jwt.feature 2 times, maybe with different arguments and then return a JSON. Pseudo-code below. First is get_jwts.js:
function fn(users) {
var jwt1 = karate.call('get_jwt.feature', users.user1);
var jwt2 = karate.call('get_jwt.feature', users.user2);
return { jwt1: jwt1, jwt2: jwt2 };
};
Then in karate-config.js
config.jwts = karate.callSingle('classpath:get_jwts.js', users);
And now you should be able to do this:
* print jwts.jwt1
* print jwts.jwt2
You can also do a feature-->feature call chain. Do let me know if this works !
EDIT: see Babu's answer in the comments, looks like you can pass an array to callSingle() ! so that may be quite convenient :)

Fetching few elements using "$.[:2]" operator throws error in karate. might be a bug

Example:
Scenario: test
* def response =
"""
[
"YEN01",
"DP258",
"SA661",
"BT202",
"UR809"
]
"""
* def subset = response.[:2]
* print subset
I tried response..[:2] . and also tried with enclosing in ().
Let me know if any one got this working.
Just adding one character will fix your problem !
* def subset = $response.[:2]
Karate defaults to JavaScript and when you want JsonPath evaluation you need to give a little hint to Karate. This is explained in the docs: https://github.com/intuit/karate#get-short-cut

Karate: How to use a Javascript function from a DIFFERENT feature file

I have created a feature file that will contain lots of javascript functions.
From within a DIFFERENT feature file I want to use ONE of those functions (and pass in a value).
How do I do this please?
My feature file is called SystemSolentraCustomKarateMethods.feature
Here is the current content (it currently contains just one function):
Feature: System Solentra Status Test
Background:
* def checkreturneddatetimeiscorrect =
#The following code compares the passed in datetime with the current systemdatetime and
#makes sure they are within 2 seconds of each other
"""
function(datetime) {
var datenow = new Date();
karate.log("***The Date Now = " + datenow.toISOString() + " ***");
var timenow = datenow.getTime();
karate.log("***The Time Now in Milliseconds = " + timenow+ " ***");
karate.log("***The Passedin Date = " + datetime + " ***");
var passedintime = new Date();
passedintime = Date.parse(datetime);
karate.log("***The Passed in Time = " + passedintime+ " ***");
var difference = timenow - passedintime;
karate.log("***The Time Difference = " + difference + " milliseconds ***");
return (difference < 2000)
}
"""
Thanks Peter I have figured out how to do this now.
(1) The feature file that contains the functions MUST have the Feature, Background and Scenario tags - even if your file does NOT contain any scenarios. (*see my example file below)
(2) In the feature file that you are calling FROM add the following code to the Background section:
* call read('yourfilename.feature')
(3) You can now use the functions within the called feature file
Here is the structure of the feature file I am calling:
Feature: Custom Karate Methods
This feature file contains Custom Karate Methods that can be called and used from other Feature Files
Background:
* def *nameofyourfunction* =
#Comment describing the fuction
"""
function() {
*code*
}
"""
****Scenario: This line is required please do not delete - or the functions cannot be called****
I think you've already seen the answer here, and this question is an exact duplicate: https://stackoverflow.com/a/47002604/143475 (edit: ok, maybe not)
Anyway, I'll repeat what I posted there:
there is no problem when you define multiple functions in one feature and call it from multiple other features
you will anyway need a unique name for each function
when you use call for that feature, all the functions will be available, yes, but if you don't use them, that's okay. if you are worrying about performance and memory, IMHO that is premature optimization
if that does not sound good enough, one way to achieve what you want is to define a Java class Foo with a bunch of static methods. then you can do Foo.myMethodOne(), Foo.myMethodTwo() to your hearts content. I would strongly recommend this approach in your case, because you seem to be expecting an explosion of utility methods, and in my experience, that is better managed in Java, just because you can maintain that code better, IDE support, unit-tests, debugging and all
Hope that makes sense !

joomla: use API inside an article

I am using the Sourcerer plugin to use PHP code inside my articles. I would like to use the Joomla API/framework inside my article to dynamically set the HTML meta tags and other stuff. I found the setHeadData method that should allow me do that but I have simply no idea as to how calling it.
[Q] Can someone give me 1 example or point me to a tutorial that would help me get started on using that joomla API/framework please?
Answer
Based on the numerous feedbacks all pointing in the same direction, using a content plugin to modify the head data is properly better than doing this via an article. If like me you want to do this in an article here is what I did:
(1) I used the snippet provided by ezpresso to set the head data inside my article.
(2) I modified the libraries/joomla/document/html/renderer/head.php file to change the way the head data was set there.
For instance you can set the title meta tag at step (1) and then at step (2) replace the following line:
$strHtml .= $tab.'<title>'.htmlspecialchars($document->getTitle()).'</title>'.$lnEnd;
with this one:
$strHtml .= $tab.'<title>'.htmlspecialchars($document['metaTags']['standard']['title']).'</title>'.$lnEnd;
You might also want to look into libraries/joomla/document/html/renderer/head.php to do some more cleanup in the head, like getting rid of the generator meta tag that Joomla inserts.
Here is the source code of the method you are referring to:
/**
* Set the html document head data
*
* #access public
* #param array $data The document head data in array form
*/
function setHeadData($data)
{
$this->title = (isset($data['title'])) ? $data['title'] : $this->title;
$this->description = (isset($data['description'])) ? $data['description'] : $this->description;
$this->link = (isset($data['link'])) ? $data['link'] : $this->link;
$this->_metaTags = (isset($data['metaTags'])) ? $data['metaTags'] : $this->_metaTags;
$this->_links = (isset($data['links'])) ? $data['links'] : $this->_links;
$this->_styleSheets = (isset($data['styleSheets'])) ? $data['styleSheets'] : $this->_styleSheets;
$this->_style = (isset($data['style'])) ? $data['style'] : $this->_style;
$this->_scripts = (isset($data['scripts'])) ? $data['scripts'] : $this->_scripts;
$this->_script = (isset($data['script'])) ? $data['script'] : $this->_script;
$this->_custom = (isset($data['custom'])) ? $data['custom'] : $this->_custom;
}
It is implemented in a JDocumentHtml class, which is located in a libraries/joomla/document/html/html.php directory.
Below is the links to some of the examples of how to use it:
setHeadData difference between j1.5 and j1.6
Remove Mootools From Joomla Header
I guess you may call the setHeadData method like this:
$doc =& JFactory::getDocument();
$options = $doc->getHeadData();
$options['metaTags'] = array("tag1", "tag2", "tag3"); // you may change the meta tags here
$doc->setHeadData($options);
Putting PHP in the article is not a very good way to accomplish what you are trying to do. Joomla frameworks has an order of operation that determines when various functions and plugins run. Due to the order of operation, there are numerous functions that will happen after an article is rendered, probably negating any changes you make from within the article. You would be better off either using an extension to handle titles and meta tags rather than trying to do it inside the article.