Selenium+PHPunit: foreach element in collection - collections

I am looking for a way to work with collections of elements in Selenium with PHPunit. Let's say I have the below code:
<div class="test">aaa</div>
<div class="test">bbb</div>
<div class="test">ccc</div>
I would like to be able to work on all the <div> elements inside an each loop, let's say by selecting the elements based on their class with //div[#class='test']
$divs = ... //
foreach ($divs as $div) {
// do something
}

The function to get the content of your entire page is :- getHtmlSource()
So your final function call to load HTML would be something like ..
$dom->loadHTML($this->getHtmlSource());

In PHP, if you want to work with some HTML data, a great solution is using the DOMDocument class -- which means being able to work with DOM methods -- via its DOMDocument::loadHTML() method.
Loading your HTML with DOMDocument :
$dom = new DOMDocument();
$dom->loadHTML('HERE YOUR HTML STRING');
You can then instanciate a DOMXpath object :
$xpath = new DOMXPath($dom);
Which will allow you to extract data from the HTML, using XPath Queries, with DOMXPath::query() :
$entries = $xpath->query('//div[#class="test"]'); // notice quotes
if ($entries->length > 0) {
foreach ($entries as $entry) {
// Work on the $entry -- which is a DOM node/element
}
}

Related

logic for parsing html using simple html dom

I have hard time parsing a structure using simple html dom
`<div class="class1">class</div>
`<div class="class2">data2</div>
`<div class="class2">data3</div>
`<div class="class1">text</div>
`<div class="class2">...</div>
Since structure is not nested so I have hard time parsing it.
I want database be grabbed as class1 as main heading and class2 as sub. So for every class1, I want to read all class2 tags until another class1 arrives. Hope it make sense.
You can check class elements to see if you reached another class1 element. Something like this explains the general idea:
$str =<<<'html'
<div class="class1">class</div>
<div class="class2">data2</div>
<div class="class2">data3</div>
<div class="class1">text</div>
<div class="class2">...</div>
html;
$html = str_get_html($str);
// Store all results in a multi dimensional array
$result = [];
$group = -1;
foreach ($html->find('div') as $elem) {
// if element class is class1, create a new array to gather data
if($elem->class == 'class1') {
$group++;
$result[$group] = [];
}
$result[$group][] = $elem->plaintext;
}
print_r($result);
So everytime we encounter a new element with class1 we start to gather information in a new array, so the final result is a nested array that groups elements the way you described.

Mink/behat iframe without id/name

I have to verify text/messages inside an iframe wihout a name/id. After exploring, I found out that solution to add id/name and use switchToIFrame(id/name). How can I set id/name to a node element in mink? SetAttribute() is not a supported method in nodelement and setValue() is not supported for a frame.
<html>
<body>
<div class="div1">
<iframe class="xyz">
<!DOCTYPE html>
<html>
<body>
<div class="frame"></div>
<p>The paragraph1</p>
</body>
</html>
</body>
</html>
My context file is
public function iShouldSeeDescription($arg1)
{
$expected = "The paragraph1";
$iframe=$this->getSession ()->getPage ()->find ( 'xpath', '//div[contains(#class,"div1")]/iframe[contains(#class,"xyz")]');
$iframe->setAttribute('id', 'iframe_id');
$iframeId = $iframe->getAttribute('id');
$this->getSession()->switchToIframe("$iframeId");
$actual = $this->getSession()->getPage()->find('xpath', '//div[#class="frame"]/p[1]');
if ($actual === null) {
throw new \InvalidArgumentException ( sprintf ( 'null:%s', $arg1 ) );
}
if (!($actual === $expected)) {
throw new \InvalidArgumentException ( sprintf ( 'The Acutal description:%s and expected description:%s did not match ', $actual, $expected ) );
}
}
What you could do to set id/name to an iframe is to use javascript.
Create a method like switchToIFrameBySelector($iframeSelector) that waits for the given element to be available then you can execute a javascript code to set a name/id for your iframe and after use switchToIFrame method.
For executing the js script you can use executeScript method in a try-catch and throw a custom Exception if needed.
public function switchToIFrame($iframeSelector){
$function = <<<JS
(function(){
var iframe = document.querySelector("$iframeSelector");
iframe.name = "iframeToSwitchTo";
})()
JS;
try{
$this->getSession()->executeScript($function);
}catch (Exception $e){
print_r($e->getMessage());
throw new \Exception("Element $iframeSelector was NOT found.".PHP_EOL . $e->getMessage());
}
$this->getSession()->getDriver()->switchToIFrame("iframeToSwitchTo");
}
Another way to set the id/name of the element in js is to use:
setAttribute('name', 'iframeToSwitchTo')
put the number or a for loop to iterate which one is required then set the number works with the cross origin
$this->getSession()->getDriver()->switchToIFrame(0);

Logic in JsViews css-tag

I am trying to put logic in a css-width in a data-link in jsViews. The two following approaches did not work:
{{for Items}}
...
<td id="show-keys" data-link="css-width{~root.ShowKeys ? '200px' : '400px'}">
or
{{for Items}}
...
<td id="show-keys" data-link="css-width{:~keysWidth()}">
...
<script type="text/javascript">
...
var app = {
...
helpers: {
showKeys: function () {
//debugging shows that this never gets fired
return app.ShowKeys ? '400px' : '100px';
}
How do I appropiatly base a css-value on a property so that it changes dynamically?
Here is a jsfiddle that shows a few variants: http://jsfiddle.net/BorisMoore/GZPvZ/
Your first expression missing a :
data-link="css-width{~root.ShowKeys ? '200px' : '400px'}"
should be
data-link="css-width{:~root.ShowKeys ? '200px' : '400px'}"
For the second approach, using a helper, apart from the naming of the helper (I think you meant it to be keysWidth - as #BKimmel points out) - you need to declare the fact that it is a computed observable with a dependency on ~root.showKeys - which you do like this:
function keysWidth() {
return app.showKeys ? '400px' : '100px';
}
keysWidth.depends = "~root.showKeys"; // or you can write ... = [app, "showKeys"]
Alternatively you can not declare the dependency, but pass it in as an argument:
data-link="css-width{:~keysWidth3(~root.showKeys)}"
Helpers can be declared globally, or passed in with the link call.
See the jsfiddle for details...
Update: There are now some new samples that cover CSS and class binding. There is a tutorial sequence on data-linking, which includes this page on data-linking class, this on on toggling class, and this one on linking to CSS attributes.
At a glance here, I notice two things;
For the second setup, I wouldn't expect that to work as written... your helper function is called showKeys, but in the template you are calling a function called keysWidth... maybe that was just a transcription mistake, but it definitely won't work like that. Change the names so they match (case-sensitive, as always in JS).
Make sure your helpers are actually getting loaded... e.g. $.helpers({helperfunction: function() {...}}) ... sometimes that can cause issues.

How to replace an existing iframe instead of appending it to the document body?

While rendering iframes, I use the code:
ifrm = document.createElement("IFRAME");
ifrm.style.width = "100%";
ifrm.style.height = 600+"px";
function makeFrame() {
document.body.appendChild(ifrm);
}
However, this keeps appending iframes below the existing one, each time I call the function makeFrame(). So, if I call makeFrame() (say thrice), then I get three iframes one below the previous. How to prevent this and replace the existing one instead of appending to the last one?
The simplest answer:
If you want to change your code as little as possible, and you're certain that you'll only have exactly one iframe on the page at any given time
function makeFrame() {
oldiframe = document.getElementsByTagName("IFRAME")[0]
// getElementsByTagName will return an array (a list).
// Adding [0] will return the first (and, under our assumptions, only) element
ifrm = document.createElement("IFRAME");
ifrm.style.width = "100%";
ifrm.style.height = 600+"px";
document.body.replaceChild(ifrm, oldiframe);
}
Create a div . Let's say div id is iframecontainer
so your code should be like:
<div id="iframecontainer">
<!-- Iframe goes here -->
</div>
Now simply getElementById with javascript and set the innerHTML to "" before appending the iframe.
Hi #mikemxm I was finally able to solve the problem. The final code looks like this:
oldiframe = document.getElementsByTagName("IFRAME")[0];
ifrm = document.createElement("IFRAME");
ifrm.style.width = "100%";
ifrm.style.height = 600+"px";
function makeFrame() {
if(oldiframe == null)
document.body.appendChild(ifrm);
else
document.body.replaceChild(ifrm, oldiframe);
}
And thanks for the warning against iframes. Do you know of any better way/technology to render content from external websites? Can there be any copyright issues? Thanks.

How to get element content by id from HTML object by JavaScript ( JQuery )

i write the following code to access page "JQueryPage.aspx" and get data from it using jQuery
<script type="text/javascript">
$.get
(
"JQueryPage.aspx",
function(data) {
alert("Data Loaded: " + data);
}
);
</script>
"JQueryPage.aspx" is just a page that contain DIV called 'resultsDIV' that contain the data that i want to return
the above code return data variable that contain "JQueryPage.aspx" html and i want to get DIV content from it ..
i have 2 questions:
1- how can i extract DIV content from data object
2- is this way is th best to get that data ?
Try using something like this:
<script type="text/javascript">
$.get
(
"JQueryPage.aspx", function(html) {
var page = $(html);
var div = $('#div1', page);
}
);
</script>
you can also look into the $.load function
Jsut wrap the data in a call to jquery and you can use it like you would normally:
$.get
(
"JQueryPage.aspx",
function(data) {
var dataDom = $(data);
$(someSelector, dataDom).each(function(){
alert($(this).html());
});
}
);
If the html markup of JQueryPage.aspx is valid xml, you can use dom parser to get the required div.
It depends - if all you want is to add the retrieved html to the existing DOM using a call to document.appendChild, yes. But if you need to parse and read values from the retrieved data, no, this is not. Pass data as a JSON string or xml.

Resources