Mink/behat iframe without id/name - iframe

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);

Related

Pop up window not appending text

I am trying to implement a 'Trace Window' pop up window when I enter a website, and then send messages to that window throughout the website in Order to diagnose some of the more awkward issues i have with the site.
The Problem is that the page changes, if The trace window already exists, all content is removed, before the new TraceText is added.
What I want is a Window that can be sent messages from any page inside the website.
I have a javascript Script debugger.js which I include as a script in every screen (shown below) I would then call the sendToTraceWindow() function to send a message to it thoughout the website. this is currently Mostly done in vbscript at present, due to the issues i am currenctly investigating.
I think it is because i am scripting in the debugger.js into every screen, which sets the traceWindow variable = null (see code below) but I do not know how to get around this!
Any help much appreciated.
Andrew
code examples:
debugger.js:
var traceWindow = null
function opentraceWindow()
{
traceWindow = window.open('traceWindow.asp','traceWindow','width=400,height=800')
}
function sendToTracewindow(sCaller, pMessage)
{
try
{
if (!traceWindow)
{
opentraceWindow()
}
if (!traceWindow.closed)
{
var currentTrace = traceWindow.document.getElementById('trace').value
var newTrace = sCaller + ":" + pMessage + "\n" + currentTrace
traceWindow.document.getElementById('trace').value = newTrace
}
}
catch(e)
{
var currentTrace = traceWindow.document.getElementById('trace').value
var newTrace = "error tracing:" + e.message + "\n" + currentTrace
traceWindow.document.getElementById('trace').value = newTrace
}
}
traceWindow.asp - just a textarea with id='trace':
<HTML>
<head>
<title>Debug Window</title>
</head>
<body>
<textarea id="trace" rows="50" cols="50"></textarea>
</body>
</HTML>
I don't think there is any way around the fact that your traceWindow variable will be reset on every page load, therefore rendering your handle to the existing window invalid. However, if you don't mind leveraging LocalStorage and some jQuery, I believe you can achieve the functionality you are looking for.
Change your trace window to this:
<html>
<head>
<title>Debug Window</title>
<script type="text/javascript" src="YOUR_PATH_TO/jQuery.js" />
<script type="text/javascript" src="YOUR_PATH_TO/jStorage.js" />
<script type="text/javascript" src="YOUR_PATH_TO/jquery.json-2.2.js" />
<script type="text/javascript">
var traceOutput;
var traceLines = [];
var localStorageKey = "traceStorage";
$(function() {
// document.ready.
// Assign the trace textarea to the global handle.
traceOutput = $("#trace");
// load any cached trace lines from local storage
if($.jStorage.get(localStorageKey, null) != null) {
// fill the lines array
traceLines = $.jStorage.get(localStorageKey);
// populate the textarea
traceOutput.val(traceLines.join("\n"));
}
});
function AddToTrace(data) {
// append the new trace data to the textarea with a line break.
traceOutput.val(traceOutput.val() + "\n" + data);
// add the data to the lines array
traceLines[tracelines.length] = data;
// save to local storage
$.jStorage.set(localStorageKey, traceLines);
}
function ClearTrace() {
// empty the textarea
traceOutput.val("");
// clear local storage
$.jStorage.deleteKey(localStorageKey);
}
</script>
</head>
<body>
<textarea id="trace" rows="50" cols="50"></textarea>
</body>
</html>
Then, in your pages where you want to trace data, you could modify your javascript like so:
var traceWindow = null;
function opentraceWindow() {
traceWindow = window.open('traceWindow.asp','traceWindow','width=400,height=800');
}
function sendToTracewindow(sCaller, pMessage) {
traceWindow.AddToTrace(sCaller + ":" + pMessage);
}
Every time a new page is loaded and the trace window is refreshed, the existing trace data is loaded from local storage and displayed in your textarea. This should achieve the functionality that you are looking for.
Please be kind on any errors - I'm winging this on a Monday morning!
Finding the jQuery library should be trivial. You can find the jStorage library here: http://www.jstorage.info/, and you can find jquery-json here: http://code.google.com/p/jquery-json/

How to determine if CSS has been loaded?

How can i Assert that the CSS for a page has successfully loaded and applied its styles in Watin 2.1?
After doing some research and writing up my answer, I stumbled upon this link that explains everything you need to know about CSS, when it is loaded and how you can check for it.
The link provided explains it so well, in fact, that I'm adding some quotes from it for future reference.
If you're curious, my answer was going to be #2 and a variation of #4.
When is a stylesheet really loaded?
...
With that out of the way, let's see what we have here.
// my callback function
// which relies on CSS being loaded function
CSSDone() {
alert('zOMG, CSS is done');
};
// load me some stylesheet
var url = "http://tools.w3clubs.com/pagr/1.sleep-1.css",
head = document.getElementsByTagName('head')[0],
link = document.createElement('link');
link.type = "text/css";
link.rel = "stylesheet";
link.href = url;
// MAGIC
// call CSSDone() when CSS arrives
head.appendChild(link);
Options for the magic part, sorted from nice-and-easy to ridiculous
listen to link.onload
listen to link.addEventListener('load')
listen to link.onreadystatechange
setTimeout and check for changes in document.styleSheets
setTimeout and check for changes in the styling of a specific element you create but style with the new CSS
5th option is too crazy and assumes you have control over the content of the CSS, so forget it. Plus it checks for current styles in a timeout meaning it will flush the reflow queue and can be potentially slow. The slower the CSS to arrive, the more reflows. So, really, forget it.
So how about implementing the magic?
// MAGIC
// #1
link.onload = function () {
CSSDone('onload listener');
};
// #2
if (link.addEventListener) {
link.addEventListener('load', function() {
CSSDone("DOM's load event");
}, false);
};
// #3
link.onreadystatechange = function() {
var state = link.readyState;
if (state === 'loaded' || state === 'complete') {
link.onreadystatechange = null;
CSSDone("onreadystatechange");
}
};
// #4
var cssnum = document.styleSheets.length;
var ti = setInterval(function() {
if (document.styleSheets.length > cssnum) {
// needs more work when you load a bunch of CSS files quickly
// e.g. loop from cssnum to the new length, looking
// for the document.styleSheets[n].href === url
// ...
// FF changes the length prematurely :(
CSSDone('listening to styleSheets.length change');
clearInterval(ti);
}
}, 10);
// MAGIC ends
There has been an update to the article lined to by #ShadowScripter. The new method purportedly works in all browsers, including FF.
var style = document.createElement('style');
style.textContent = '#import "' + url + '"';
var fi = setInterval(function() {
try {
style.sheet.cssRules; // <--- MAGIC: only populated when file is loaded
CSSDone('listening to #import-ed cssRules');
clearInterval(fi);
} catch (e){}
}, 10);
document.getElementsByTagName('head')[0].appendChild(style);
After page load you can verify the style on some of your elements something like this:
var style = browser.Div(Find.ByClass("class")).Style;
Assert.That(Style.Display, Is.StringContaining("none"));
Assert.That(Style.FontSize, Is.EqualTo("10px"));
And etc...
Since browser compatibility can vary, and new future browser standards subject to change, I would recommend a combination of the onload listener and adding CSS to the style sheet so you can listen for when the HTML elements z-index changes if you are using a single style sheet. Otherwise, use the function below with a new meta tag for each style.
Add the following to the CSS file that you are loading:
#*(insert a unique id for he current link tag)* {
z-index: 0
}
Add the following to your script:
function whencsslinkloads(csslink, whenload ){
var intervalID = setInterval(
function(){
if (getComputedStyle(csslink).zIndex !== '0') return;
clearInterval(intervalID);
csslink.onload = null;
whenload();
},
125 // check for if it has loaded 8 times a second
);
csslink.onload = function(){
clearInterval(intervalID);
csslink.onload = null;
whenload();
}
}
Example
index.html:
<!doctype html>
<html>
<head>
<link rel=stylesheet id="EpicStyleID" href="the_style.css" />
<script async href="script.js"></script>
</head>
<body>
CSS Loaded: <span id=result>no</span>
</body>
</html>
script.js:
function whencsslinkloads(csslink, whenload ){
var intervalID = setInterval(
function(){
if (getComputedStyle(csslink).zIndex !== '0') return;
clearInterval(intervalID);
csslink.onload = null;
whenload();
},
125 // check for if it has loaded 8 times a second
);
csslink.onload = function(){
clearInterval(intervalID);
csslink.onload = null;
whenload();
}
}
/*************************************/
whencsslinkloads(
document.getElementById('EpicStyleID'),
function(){
document.getElementById('result').innerHTML = '<font color=green></font>'
}
)
the_style.css
#EpicStyleID {
z-index: 0
}
PLEASE do not make your script load synchronously (without the async attribute) just so you can capture the link's onload event. There are better ways, like the method above.

Selenium+PHPunit: foreach element in collection

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
}
}

HTML: how to change my website background at each visit?

what's the best approach to change the background of my website at each visit ?
1) write php code, loading a random css file containing the background property
2) write php code, generating different html (and including the background property directly into html code
3) something else ?
thanks
This can be done in your theme's page.tpl.php variable preprocessor. Store the random style in the $_SESSION array to re-use for all pages in the same user session. And append the markup to the $head variable used in the template.
YOURTHEME_preprocess_page(&$variables) {
$style = $_SESSION['YOURTHEME_background_style'];
if (!$style) {
$style = array();
//Generate your random CSS here
$style = "background-image: url('bg-". rand(0,10) .".png')";
$_SESSION['YOURTHEME_background_style'] = $style;
}
$variables['head'] .= '<style type="text/css">body {'. implode("\n", $style) .'}</style>';
}
Usually, $head is placed before $style in the page.tpl.php templaye, so CSS rules from any .css files will overrides your random rule. You may have to use !important in your random CSS to avoid this.
I would probably:
Use hook_user op login to detect the login and then store the background color code in the user object.
In your page template create an inline style for the background color that uses the value stored on the user object. For anonymous users don't do anything and have default defined in a style sheet.
Use a session cookie. Could be set either via js (client side) or something like php (server-side). Here's an example of a js-only solution:
<!doctype html>
<html><head><script>
var backgrounds=['foo.png', 'bar.png', 'hahah.png'];
function setBg () {
var currentBg=readCookie('which_bg');
if (!currentBg) {
currentBg=backgrounds[Math.random()*backgrounds.length|0];
createCookie('which_bg', currentBg);
}
document.body.style.backgroundImage='url('+currentBg+')';
}
// from http://www.quirksmode.org/js/cookies.html
function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name,"",-1);
}
</script></head>
<body onload="setBg();">
...
</body></html>
To change the background image at each page load (not exactly "visit" though), you can use the Drupal module Dynamic Background. For Drupal 7, only the 7.x-2.x branch contains the option for cycling backgrounds randomly. You would install it with:
drush dl dynamic_background-7.x-2.x && drush en dynamic_background
The feature can also be added to the 7.x-1.x branch with a patch, and to the 6.x-1.x branch similarly.

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