I would like to determine if particular elements on a page are visible when printed as controlled by CSS #media rules.
Is there a way to do this with Selenium?
I know there is the isDisplayed method, which takes the CSS into account, but there is nothing I can find to tell Selenium which media type to apply.
Is there a way to do this?
Or is there another way to test web pages to make sure the elements you want are printed (and those you don't aren't)?
Update:
For clarity, there are no plans to have a javascript print button. The users will print using the normal print functionality of the browser (Chrome, FF and IE). #media css rules will be used to control what is shown and hidden. I would like Selenium to pretend it is a printer instead of a screen, so I can test if certain elements will be visible in what would be the printed version of the page.
I've managed to write a script that does just what you want: it hides screen-only styles and sets print-only styles to be screen-only.
You need to inject the following JavaScript with Selenium:
(function pretendToBeAPrinter() {
//For looking up if something is in the media list
function hasMedia(list, media) {
if (!list) return false;
var i = list.length;
while (i--) {
if (list[i] === media) {
return true;
}
}
return false;
}
//Loop though all stylesheets
for (var styleSheetNo = 0; styleSheetNo < document.styleSheets.length; styleSheetNo++) {
//Current stylesheet
var styleSheet = document.styleSheets[styleSheetNo];
//Output debug information
console.info("Stylesheet #" + styleSheetNo + ":");
console.log(styleSheet);
//First, check if any media queries have been defined on the <style> / <link> tag
//Disable screen-only sheets
if (hasMedia(styleSheet.media, "screen") && !hasMedia(styleSheet.media, "print")) {
styleSheet.disabled = true;
}
//Display "print" stylesheets
if (!hasMedia(styleSheet.media, "screen") && hasMedia(styleSheet.media, "print")) {
//Add "screen" media to show on screen
styleSheet.media.appendMedium("screen");
}
// Get the CSS rules in a cross-browser compatible way
var rules;
try {
rules = styleSheet.cssRules;
} catch (error) {
console.log(error);
}
try {
rules = styleSheet.rules;
} catch (error) {
console.log(error);
}
// Handle cases where styleSheet.rules is null
if (!rules) {
continue;
}
//Second, loop through all the rules in a stylesheet
for (var ruleNo = 0; ruleNo < rules.length; ruleNo++) {
//Current rule
var rule = rules[ruleNo];
//Hide screen-only rules
if (hasMedia(rule.media, "screen") && !hasMedia(rule.media, "print")) {
//Rule.disabled doesn't work here, so we remove the "screen" rule and add the "print" rule so it isn't shown
console.info('Rule.media:');
console.log(rule.media)
rule.media.appendMedium(':not(screen)');
rule.media.deleteMedium('screen');
console.info('Rule.media after tampering:');
console.log(rule.media)
}
//Display "print" rules
if (!hasMedia(rule.media, "screen") && hasMedia(rule.media, "print")) {
//Add "screen" media to show on screen
rule.media.appendMedium("screen");
}
}
}
})()
You can see it in action at JSFiddle.
Bookmarklet
You can also install it as a bookmarklet.
More information:
About mediaList
About document.styleSheets
Note: I've only tested this in Google Chrome and Mozilla Firefox. It may or may not work in other browsers.
There is some cases that it can be useful to use visual automation tools such as applitools.
We implements it in some of our tests, and it's great so far.
//jquery
function printDetail() {
window.print();
}
//html
<button type="button" class="btn" value="Print Div" onclick="printDetail()"><i class="icon-print"></i> Print</button>
//css
#media print{
.header{display:none;}
.footer{display:none;}
.leftside{display:none;}
.rightside{display:block;}
}
// http://jsfiddle.net/kisspa/52H7g/
I think I have a little clever way to accomplish this:
Can I assume that the PRINT button is going to be on the html page as is the case in the jsfiddle.net link above?
Basically, can I EXCLUDE the FILE->PRINT or RIGHT CLICK->PRINT options and only assume that the only way someone can print your page is by clicking on a print button embedded in your html page as shown in the jsfiddle link above if not what are other test cases?
Finally, can I assume that your selenium tests will ONLY run in the Chrome browser and not firefox? This is important because the PRINT command behaves different in Chrome as it does in Firefox. My fix will only work w/ Chrome.
Related
I have an animated gif that works perfectly in all browsers except ie (surprise, surprise). After much searching I have found the gif is not animated in ie due to a setting in the options:
Settings -> Advanced settings -> Multimedia -> Play animations in webpages
Is there any way to detect if this is enabled as I would like to display something else instead of a static loading gif, or is there a way to force ie to play the animation?
In case there is a workaround, here is the code I use to show my loader and the type of gif I am using:
#loading {
background:url(http://preloaders.net/preloaders/712/Floating%20rays.gif) center center no-repeat;
position:fixed;
left:0;
right:0;
bottom:0;
top:0;
}
<div id="loading"></div>
You can use root class from body which shows only IE browser then write like as below in your css:
.rootclassname #loading {
background : //use static image here
}
Hmmm, seems a bit dirty but what the hell, punish the stupid ie users...
As a workaround, I did a mixture of Prajwal and lonut's answers - adding an ie class to my loader I then saved out each part of the animated gif and then used the following js to give me an animated loader for ie:
var ieLoadingCount = 1,
ieLoadingInt;
function addLoading() {
var loading = $('#paving-designer-loading');
if (loading.hasClass('ie')) { // only do this for ie
clearInterval(ieLoadingInt); // need to clear interval as this is a multi-step form and addLoading may be called multiple times
ieLoadingInt = setInterval(function () { animateIELoading(loading, true); }, 175); // preload images
}
}
function animateIELoading(loading, firstRun) {
loading.css('background-image', 'url(' + baseUrl + 'images/presentation/toolbox/pavingdesigner/loading/' + ieLoadingCount + '.png)');
if (ieLoadingCount == 12) { // loading gif had 12 parts in it
ieLoadingCount = 1;
if (firstRun) {
clearInterval(ieLoadingInt); // finish preload
ieLoadingInt = null;
}
} else {
ieLoadingCount++;
}
}
function showLoading(loading) {
if (loading.hasClass('ie')) {
clearInterval(ieLoadingInt);
ieLoadingInt = setInterval(function () { animateIELoading(loading, false); }, 175);
}
loading.show();
}
function hideLoading(loading) {
loading.hide();
if (loading.hasClass('ie')) {
clearInterval(ieLoadingInt);
}
}
I'll leave this open in case anyone can find a way to check if the animation is allowed in the first place as currently I apply this for all ie users regardless of if the animation is allowed or not. Would be good to only apply it to the browsers that have their animations turned off.
Title pretty much says it all.
I'm struggling with selecting the damn fullscreen button out of the default <video> skin.
I found this on http://www.jwplayer.com/blog/using-the-browsers-new-html5-fullscreen-capabilities/:
<script type="text/javascript">
function goFullscreen(id) {
// Get the element that we want to take into fullscreen mode
var element = document.getElementById(id);
// These function will not exist in the browsers that don't support fullscreen mode yet,
// so we'll have to check to see if they're available before calling them.
if (element.mozRequestFullScreen) {
// This is how to go into fullscren mode in Firefox
// Note the "moz" prefix, which is short for Mozilla.
element.mozRequestFullScreen();
} else if (element.webkitRequestFullScreen) {
// This is how to go into fullscreen mode in Chrome and Safari
// Both of those browsers are based on the Webkit project, hence the same prefix.
element.webkitRequestFullScreen();
}
// Hooray, now we're in fullscreen mode!
}
</script>
<img class="video_player" src="image.jpg" id="player"></img>
<button onclick="goFullscreen('player'); return false">Click Me To Go Fullscreen! (For real)</button>
I see you're probably looking for the native player's selector, but this will let you create your own button.
I am working on a code that checks if the browser supports Media Queries. If it does, it then checks the window width and if it falls under 700px it loads a CSS file, but if the window width resizes and goes back to something wider than 700px, the CSS file does not "unload" and thus, it looks bad. Can you please help me understand what and how is the best way to use this?
Here's my code:
function check_media_query_support() {
if (!Modernizr.mq('only all')) {
if ($(window).width() <= 700) {
Modernizr.load({
load:'../styles/jquery-ui/test_unsupported_mq_700.css'
});
} else {
}
if ($(window).width() <= 400) {
Modernizr.load({
load: '../styles/jquery-ui/test_unsupported_mq_400.css'
});
}
}
}
function resizeUi() {
check_media_query_support();
}
Modernizr won't listen to window size changes, with the functionality you are looking for, you actually probably want a responsive polyfill, like respond.js
When messing around in the FireBug css panel, you change the their representation of the original css file. Like:
.myCssClass { width: 100px; }
However, if you add a jQuery line to this,
$(".myCssClass").css("width", "200px");
you end (of course) up with changing the style tag for this element and you see that your original width:100px has a strikethough in the FireBug representation.
So my question is, do you know a way to change the "original" width:100px instead of changing the style tag. I guess you have to through a FireBug extension to access that property, and that is not a problem for me. But I don't know where to start :)
Edit: Have to point out that I am need to change the property by code! Either from a FireBug extension or somehow reload the corresponding css so that FireBug think it is the orginal value.
Here is an old JS function that usually worked well for me (Before Stylish and Greasemonkey).
Note that plain JS has security restrictions from accessing some stylesheets. A FF add-on can get around that, but then you need to also beware of corrupting browser-chrome styles.
function replaceStyleRuleByName (sStyleName, sNewRule)
{
var iNumStyleSheets = document.styleSheets.length;
var bDebug = 0;
if (bDebug) console.log ('There are ' + iNumStyleSheets + ' style sheets.');
for (iStyleS_Idx=0; iStyleS_Idx < iNumStyleSheets; iStyleS_Idx++)
{
var iNumRules = 0;
var zStyleSheet = document.styleSheets[iStyleS_Idx];
if (zStyleSheet)
{
/*---WARNING!
This next line can throw an uncaught exception!
Error: uncaught exception:
[Exception... "Access to restricted URI denied" code: "1012"
nsresult: "0x805303f4 (NS_ERROR_DOM_BAD_URI)"
location: ... ...]
*/
//--- try/catch for cross domain access issue.
try
{
var zRules = zStyleSheet.cssRules;
if (zRules)
{
iNumRules = zRules.length;
}
}
catch (e)
{// Just swallow the error for now.
}
}
if (bDebug) console.log ("Style sheet " + iStyleS_Idx + " has " + iNumRules + " ACCESSIBLE rules and src: " + zStyleSheet.href);
//for (var iRuleIdx=iNumRules-1; iRuleIdx >= 0; --iRuleIdx)
for (var iRuleIdx=0; iRuleIdx < iNumRules; ++iRuleIdx)
{
if (zRules[iRuleIdx].selectorText == sStyleName)
{
zStyleSheet.deleteRule (iRuleIdx);
if (bDebug) console.log (sNewRule);
if (sNewRule != null)
{
zStyleSheet.insertRule (sStyleName + sNewRule, iRuleIdx);
}
//return; //-- Sometimes changing just the first rule is not enough.
}
}
//--- Optional: Punt and add the rule, cold, to any accessible style sheet.
if (iNumRules > 0)
{
if (sNewRule != null)
{
try
{
zStyleSheet.insertRule (sStyleName + sNewRule, iRuleIdx);
}
catch(e)
{// Just swallow the error for now.
}
}
}
}
return;
}
Sample Usage:
replaceStyleRuleByName ('body', '{line-height: 1.5;}' );
replaceStyleRuleByName ('#adBox', '{display: none;}' );
replaceStyleRuleByName ('.BadStyle', null );
Just right click on the property in question and then edit [stylename]
Look for the "Computed" tab, it displays the actual values used of the properties of an element. The "Style" tab only displays the "stylesheet values" that affects a particular element, which may or may not be actually used by Firefox due to CSS' cascading rule and other layouting considerations.
Is there a tool that will find for me all the css classes that I am referencing in my HTML that don't actually exist?
ie. if I have <ul class="topnav" /> in my HTML and the topnav class doesn't exist in any of the referenced CSS files.
This is similar to SO#33242, which asks how to find unused CSS styles. This isn't a duplicate, as that question asks which CSS classes are not used. This is the opposite problem.
You can put this JavaScript in the page that can perform this task for you:
function forItems(a, f) {
for (var i = 0; i < a.length; i++) f(a.item(i))
}
function classExists(className) {
var pattern = new RegExp('\\.' + className + '\\b'), found = false
try {
forItems(document.styleSheets, function(ss) {
// decompose only screen stylesheets
if (!ss.media.length || /\b(all|screen)\b/.test(ss.media.mediaText))
forItems(ss.cssRules, function(r) {
// ignore rules other than style rules
if (r.type == CSSRule.STYLE_RULE && r.selectorText.match(pattern)) {
found = true
throw "found"
}
})
})
} catch(e) {}
return found
}
Error Console in Firefox. Although, it gives all CSS errors, so you have to read through it.
IntelliJ Idea tool does that as well.
This Firefox extension is does exactly what you want.
It locates all unused selectors.