Protractor: How do I get the current browser width? - javascript

I want some helper function code to run based on whether the browser is in "desktop" mode, which is default, or "mobile" mode, which some specs require for mobile only functionality. Height is always 800, but width can be 600 or 1280.
login: function() {
var self = this;
var browserSize = browser.manage().window().getSize().then(function(size) {
// size is still an unresolved promise ;.;
return size;
});
// Go to login page
browser.get(browser.baseUrl); // Will redirect authed users to the application
// If not at the login page, logout first
if (browser.getCurrentUrl() !== browser.baseUrl) {
if (browserSize.width == 600) {
self.mobileLogout();
} else {
self.desktopLogout();
}
}
self.loginPage.login();
}
How do I either resolve the promise getSize returns or determine the browser width some other way?

You were so very close! This worked for me just now:
var browserSizeOfMe = browser.driver.manage().window().getSize().then(function(size) {
console.log(" BROWSER SIZE "+ JSON.stringify(size) );
return size;
});

You don't have to approach the problem of differentiating mobile and desktop judging by the size of the browser window. Instead, you can access the configured capabilities via getCapabilities(), please see examples at:
Protractor: accessing capabilities

you can get the browser width using this script
var width = window.innerWidth || document.body.clientWidth;
but i dont think this answers your problem

There was indeed too many promises going on in this function. I had to nest needing the width and the current url so things would be defined and usable at the correct times.
Roughly the working code:
login: function() {
var self = this;
browser.get(browser.baseUrl);
browser.getCurrentUrl().then(function(url) {
if(url !== browser.baseUrl) {
browser.manage().window().getSize().then(function(size) {
if (size.width == 600) {
self.mobileLogout();
} else {
self.desktopLogout();
}
}
}
self.loginPage.login();
}
}

It can be done using "viewport", have a look following link
http://www.w3schools.com/css/css_rwd_viewport.asp

Related

A better method to detect phone notch?

I have a method to detect the notch on iPhones and it works...but I just found out it doesn't work well. About 1 out of 5 app starts, I see the function reporting that the phone doesn't have the notch when in fact it does.
When my app deviceReady() state is activated, I then check to see if the device has the notch then assign the true/false value to a variable that I use later on. As mentioned, about 1 in 5 times it returns false. I think its a timing thing, the function is evaluating the DOM before the DOM has fully loaded causing the detection method to fail.
function hasNotch() {
if (CSS.supports('padding-bottom: env(safe-area-inset-bottom)')) {
var div = document.createElement('div');
div.style.paddingBottom = 'env(safe-area-inset-bottom)';
document.body.appendChild(div);
setTimeout(function() {
var calculatedPadding = parseInt(window.getComputedStyle(div).paddingBottom, 10);
document.body.removeChild(div);
if (calculatedPadding > 0) {
errMgmt("preIndex/hasNotch ",101.1,"READY: Notch Detected") ;
return true ;
} else {
errMgmt("preIndex/hasNotch ",101.2,"READY: Notch Not Detected") ;
return false ;
}
},100) ;
} else {
errMgmt("preIndex/hasNotch ",101.3,"READY: Notch Not Supported") ;
return false ;
}
}
$ionicPlatform.ready(function() {
$rootScope.hasNotch = hasNotch() ;
...
}) ;
Then in my landing page controller:
.controller('HomeCtrl', function($rootScope,$scope,$state,$stateParams,$ionicModal,$q,apiService) {
if ($rootScope.hasNotch == true) {
.. do css stuff ..
}
}) ;
When it fails to detect, its always the message READY: Notch Not Detected returned...not once has it returned the READY: Notch Not Supported which means the CSS.supports is working, but not part regarding the calculatedPadding.
The true/false value is needed in the Controller of the landing page, and when it fails its causing CSS layout issues. Either the hasNotch() is too slow and the app init is triggering the landing page faster than the response from hasNotch() or there is actually something else wrong with my function.
On a side note, I have researched different methods to detect the notch on Android phones - the two plugins seem to have issues and/or aren't really supported anymore. Was hoping for a solid solution for Android....or even better, a universal solution for both iOS types.
I came up with a solution but I still hoping there is a better way. Something that can detect a phone notch during app initialization versus having to rely on DOM evaluations. I have tested the app over 25 times and so far this new method has detected the notch every single time. As I code other things I will continue to evaluate the outcome, but so far so good. Here is what I did.
I removed the check from deviceReady:
$ionicPlatform.ready(function() {
$rootScope.hasNotch = hasNotch() ;
...
}) ;
And in my landing page controller:
.controller('HomeCtrl', function($rootScope,$scope,$state,$stateParams,$ionicModal,$q,apiService) {
$scope.hasNotch = null ;
var watchNotch = $scope.$watch('hasNotch',function() {
//begin watching for var change
deviceData.hasNotch = $scope.hasNotch ;
$scope.doSomething() ;
watchNotch() ; // kill watch
}) ;
if (ionic.Platform.isIOS()) {
$scope.hasNotch = hasNotch() ;
} else {
deviceData.hasNotch = false ;
$scope.doSomething() ;
watchNotch() ; // kill notch
}
}) ;
Now I just need to go find a reliable plugin to detect Android notches....easier said than done.

Combine Multiple JS files into one

I have a URL redirection JS code on a number of pages i.e Mon.html, Tue.html etc, I want to combine it into one file and still redirect the user to the mobile version of the page depending on their screen width e.g
if (screen.width <= 800) {
window.location = "../m/days/mon.html";
}
This means that I have many JS files with a respective window.location
I'm trying to have one JS file with all the necessary redirects.
You can map current location to a redirect.
if(screen.width <= 800) {
switch(window.location) {
case firstLocation:
window.location = firstRedirect;
case secondLocation:
window.location = secondRedirect;
// other redirect cases
default:
throw new Error('no redirect assigned to current location');
}
}
Or if redirects has the same pattern you can modify current location according to that pattern. That would be more preferable, as it doesn't contain lots of hardcoded locations, so you don't have to update it manually every time.
if (screen.width <= 800) {
window.location = '../m/' + window.location;
}
Figured it out.
var screen_width = screen.width;
$(function() {
if ($('body').is('.index') && (screen_width <= 800)) {
window.location = "m/index.html";
}
});

scroll within a scrollable element with webdriver-io?

How do you scroll within a scrollable element with webdriver-io?
I have tried the following code:
client
.scroll('#hierarchy_p')
.scroll(20, 50);
Or
client
.scroll('#hierarchy_p', 20, 50);
But neither of them have any effect.
Normally, I wouln't advice using driver.executeScript, but until something like webElement.setAttribute comes up, I doubt that there are many other ways of doing this.
for scrolling up and down a scrollable element:
function scrollToFn(driver, element, scrollAmount){
return elem.getAttribute('scrollTop').then(function(val){
scrollAmount += +val; // written as +val for string to number conversion
return driver.executeScript("arguments[0].scrollTop = arguments[1]", elem, scrollAmount);
});
}
for scrolling to particular element inside scrollable element:
function scrollToInnerFn(driver, parentEle, innerEle){
return innerEle.getAttribute('offsetTop').then(function(val){
return driver.executeScript("arguments[0].scrollTop = arguments[1]", parentEle, val);
});
}
Note: both the above functions would be returning a promise.
usage
...
var webdriver = require('selenium-webdriver');
var browser1 = new webdriver.Builder().usingServer().withCapabilities({
browserName: 'firefox'
}).build();
...
var elem = browser1.findElement(webdriver.By.css('#scrollT'));
var elem2 = browser1.findElement(webdriver.By.css('#mm'));
scrollToFn(browser1, elem, 200).then(function(){
scrollToInnerFn(browser1, elem, elem2);
}).then(...
The scrollTop approach did not work for my use case. This is what worked:
browser.execute(function() {
document.querySelector('#hierarchy_p').scrollIntoView();
});
This is using webdriver.io v4.14.0 testing in Chrome.
I know this post is old, but I had the same problem. The examples given by #mido are quite complex and I had hard time understanding them, so I found a simple way to do this.
We have to use the .execute() command:
browser.execute([function(){},param1,param2,....]);
You want to scroll down inside the scroll-able element, so let's suppose your container is a div with id id='scrollContent_body'. Now all you have to do is use below the snipped presented bellow:
browser.execute(function() {
// browser context - you may not access client or console
// += 60 will scroll down
// -= 60 will scroll up
document.getElementById('scrollContent_body').scrollTop += 60;
});
Note: It does not matter weather your driver is browser, or client.

Stop/start window resize when viewport width reaches some value

The goal
If user's viewport reaches some value, I need to stop or start:
jQuery(window).on('resize'){ // ... }
The scenario
Let's suppose that we have the following array (in JavaScript):
var ranges = [1024, 1280];
I'm using jQuery(window).on('resize'){} with some ifs inside to do things when user's viewport raches a width between the values set by ranges' array.
But I just want to run those "things" when the viewport is out or in range with the widths from the array. I mean, jQuery(window).on('resize'){} should stop to run when the user's viewport is between 1024px x 1280pxof width, but its need to wake up when the viewport is out of range to do another thing that I want.
Playground
To have a better comprehension of the problem, open your console and take a look in this jsFiddle.
You'll see that the console prints
"Hi" for each time that jsFiddle JavaScript's Window has its width changed, and I want to display it once.
Oh my god, are you stupid?! Your script is wrong because nothing is printing here. Calm down, fella! jsFiddle doesn't interpret $(window) as your browser's window. To run the function, you should to resize result window. Look:
What I'm expecting, actually
Based on the above's script, I want to see Hi! just when the viewport enters in some width between 1024px x 1280px — once. When the viewport is outside, nothing happens, but when viewport enters in the specified width again, prints Hi! — again and once.
What have I tried?
Actually, I'm stuck. My mind can't think in the solution — I need a light!
Doubts? I haven't made clear enough?
Comment your question, please!
Duplicated?
Post the link or marks as duplicate — I didn't saw any similar topic like this before (sorry for this).
You just need to keep track of the state. Here's a Fiddle that I think does what you want:
var isInRange = (function() {
var test = function() {
return $(window).width() >= 1024 && $(window).width() <= 1280;
};
var current = test();
return function() {
if (test()) {
if (!current) {
current = true;
console.log('Hi!');
}
} else {
current = false;
}
}
}())
$(window).on('resize', isInRange);
Update
Okay, after all these comments, I'm really not happy with the code as written. Here's another version of the same ideas, cleaning up some of the code and adding the onExit functionality too:
var isInRange = (function() {
var $window = $(window);
var test = function() {
var ww = $window.width();
return ww >= 1024 && ww <= 1280;
};
var inRange = test();
var onEnter = function() {
console.log("Hi");
};
var onExit = function() {
console.log("Bye");
};
return function() {
test() ? (!inRange && (inRange = true) && onEnter())
: ((inRange && onExit()) || (inRange = false));
};
}())
$(window).on('resize', isInRange);
It's a little more clean, a little more organized, and slightly more efficient. But nothing really substantive has changed.
You'll need to use a flag to keep track of the state, I've updated your JSFiddle as follows:
var inRange;
function isInRange() {
if ($(window).width() >= 1024 && $(window).width() <= 1280){
if (!inRange){
inRange = true;
console.log('Hi!');
}
} else {
inRange = false;
}
}
$(window).on('resize', isInRange).trigger('resize');
EDIT
To show a different message only once depending on whether the window is inside or outside the set range, simply keep track of two states with flags like so:
var inRange, outsideRange;
function isInRange() {
if ($(window).width() >= 1024 && $(window).width() <= 1280){
outsideRange = false;
if (!inRange){
inRange = true;
console.log('Hi!');
}
} else {
inRange = false;
if (!outsideRange){
outsideRange = true;
console.log('Bye!');
}
}
}
$(window).on('resize', isInRange).trigger('resize');
Here is the updated JSFIddle.
I hope this helps!
just tell it when to go
var min=1024, max=1280, go;//values
$(window).on('resize', function() {//on resize
var ww = $(window).width();//get window width
if (go && ww >= min && ww <= max) {//go & in range
console.log('Hi!');//print 'Hi!'
go = false;//!go
} else if (!go && (ww<min || ww>max)) {//!go & out of range
console.log('Bye!');//print 'Bye!'
go = true;//go
}
});
made a fiddle: http://jsfiddle.net/filever10/zP39t/

Javascript from file gives Uncaught ReferenceError

I am trying to dynamically adjust the height of an iFrame on a web page depending on the content within the iFrame via some JavaScript.
My problem is when I have the script directly on the page in a <script> tag it works fine. When I stuff the code in to a separate js file and link to it- it doesn't work!
<iframe id='StatusModule' onload='FrameManager.registerFrame(this)' src='http://randomdomain.dk/StatusModule.aspx'></iframe>
<script type='text/javascript' src='http://randomdomain.dk/FrameManager.js'></script>
It gives me the error:
Uncaught ReferenceError: FrameManager is not defined
Can this really be true? Has it something to do with the page life cycle?
Ps. I guess the JavaScript code is irrelevant, as we not it works.
UPDATE: I think this might have something to do with secure http (https) and the different browsers in some weird way. I noticed that the script actually worked in Firefox. Or rather I'm not sure if its the script, or just Firefox's functionality that resizes iframes automatically depending on the content. It doesn't give me any error though.
If I then add https to the script url reference, the scripts work in IE and Chrome - but not in Firefox. Function reference error! This just got weird!
UPDATE #2: Its not a Firefox function that resizes the iframe. Its the actual script that works (without https).
UPDATE #3: The JavaScript. Works fine if I put it directly into a script tag.
var FrameManager = {
currentFrameId: '',
currentFrameHeight: 0,
lastFrameId: '',
lastFrameHeight: 0,
resizeTimerId: null,
init: function () {
if (FrameManager.resizeTimerId == null) {
FrameManager.resizeTimerId = window.setInterval(FrameManager.resizeFrames, 0);
}
},
resizeFrames: function () {
FrameManager.retrieveFrameIdAndHeight();
if ((FrameManager.currentFrameId != FrameManager.lastFrameId) || (FrameManager.currentFrameHeight != FrameManager.lastFrameHeight)) {
var iframe = document.getElementById(FrameManager.currentFrameId.toString());
if (iframe == null) return;
iframe.style.height = FrameManager.currentFrameHeight.toString() + "px";
FrameManager.lastFrameId = FrameManager.currentFrameId;
FrameManager.lastFrameHeight = FrameManager.currentFrameHeight;
window.location.hash = '';
}
},
retrieveFrameIdAndHeight: function () {
if (window.location.hash.length == 0) return;
var hashValue = window.location.hash.substring(1);
if ((hashValue == null) || (hashValue.length == 0)) return;
var pairs = hashValue.split('&');
if ((pairs != null) && (pairs.length > 0)) {
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
if ((pair != null) && (pair.length > 0)) {
if (pair[0] == 'frameId') {
if ((pair[1] != null) && (pair[1].length > 0)) {
FrameManager.currentFrameId = pair[1];
}
} else if (pair[0] == 'height') {
var height = parseInt(pair[1]);
if (!isNaN(height)) {
FrameManager.currentFrameHeight = height;
//FrameManager.currentFrameHeight += 5;
}
}
}
}
}
},
registerFrame: function (frame) {
var currentLocation = location.href;
var hashIndex = currentLocation.indexOf('#');
if (hashIndex > -1) {
currentLocation = currentLocation.substring(0, hashIndex);
}
frame.contentWindow.location = frame.src + '&frameId=' + frame.id + '#' + currentLocation;
}
};
window.setTimeout(FrameManager.init, 0);
UPDATE #4: Alright I did as ShadowWizard and TheZuck suggested:
<script type="text/javascript">
var iframe = document.createElement("iframe");
iframe.src = "http://www.randomdomain.dk/StatusWebModule.aspx";
iframe.width = '100%';
iframe.id = 'StatusModule';
iframe.scrolling = 'no';
if (iframe.attachEvent) {
iframe.attachEvent("onload", function () {
FrameManager.registerFrame(iframe);
});
} else {
iframe.onload = function () {
FrameManager.registerFrame(iframe);
};
}
document.getElementById('framecontainer').appendChild(iframe);
</script>
With HTTP as URL its work on IE and Firefox - not Chrome. If I set it to HTTPS it works on Chrome and IE - Not Firefox. Same error:
"ReferenceError: FrameManager is not defined".
What is going on here?
a couple of things:
I would bet on a race condition when you have two independent
resources which are supposed to be loaded concurrently. You can
easily check this by writing to log (or to document, whichever works
for you) when both finish loading (i.e. add a little script in the
iframe to dynamically add the time to the content or write to log if
you're using chrome, do that in the external script file as well,
and see if they post the time in a specific order when this fails). In your case, if the script appears before the iframe, and you don't mark it as async, it should be loaded before the iframe is fetched, so it would seem strange for the iframe not to find it due to a race condition. I would bet on (3) in that case.
Assuming there is such an issue (and if there isn't now, when you go
out into the real world it will be), a better way to do this is to
make sure both behave well in case the other loads first. In your
case, I would tell the iframe to add itself to a local variable
independent of the script, and would tell the script to check if the
iframe registered when it loads, and after that in recurring
intervals until it finds the iframe.
If the page the script is loaded into is not in the same domain
as the iframe (note that it doesn't matter where the script comes
from, it only matters what the page's domain is), (or even the same
protocol as someone mentioned here), you will not be able to access
the content so you won't be able to resize according to what the
content is. I'm not sure about the onload method, if it's considered part of the wrapping page or part of the internal iframe.
Check out this question, it sounds relevant to your case:
There's also an interesting article here about this.
I think that your frame is loaded before the script, so "FrameManager" does not exist yet when the iframe has finished loading.

Categories