Selenium WebdriverJS, how to use PageFactory? - javascript

I am currently trying to start writing my tests using TypeScript(solution in JavaScript is also fine) and I am having a hard time understanding how to use the PageFactory from C#.
In C# I used to write separate classes for every page/form of the website that I am testing, for example:
public class RegisterForm
{
public RegisterForm()
{
PageFactory.InitElements(Driver.Chrome, this);
}
[FindsBy(How = How.CssSelector, Using = #"........")]
public IWebElement EmailField { get; set; }
}
And whenever I needed to use elements from the RegisterForm I was initializing this class and using them from here. How can I do something like this in TypeScript or in JavaScript?

An example of how I created my page objects using protractor on a non-angular page is below
'use strict';
var DocumentationPage = function () {
var documentationLink = element(by.css(".nav-global a[href*='Docs'"));
var documentationURL = "https://docs.docker.com/"
var installButton = element(by.buttonText("Install"));
var dockerEngineButton = element(by.buttonText("Docker Engine"));
var dockerLinuxButton = element(by.buttonText("Linux"));
var dockerCloudButton = element(by.buttonText("Cloud"));
var dockerFundamentalsButton = element(by.buttonText("Docker Fundamentals"));
var useDockerButton = element(by.buttonText("Use Docker"));
var amazonInstallLink = element(by.partialLinkText("Amazon EC2 Installation"));
this.go = function(){
browser.get(documentationURL);
browser.sleep(100);
};
this.drillDownToAmazonInstallDocumentation = function(){
installButton.click();
browser.sleep(100);
dockerEngineButton.click();
browser.sleep(100);
dockerCloudButton.click();
browser.sleep(100);
};
this.clickInstallOnAmazonLink = function(){
amazonInstallLink.click();
browser.sleep(100);
};
this.drillUpToAmazonInstallDocumentation = function(){
dockerCloudButton.click();
browser.sleep(100);
dockerEngineButton.click();
browser.sleep(100);
installButton.click();
browser.sleep(2000);
};
};
module.exports = DocumentationPage;
I used the page objects in tests as demonstrated below.
var HomePage = require("./pages/HomePage.js");
var DocumentationPage = require('./pages/DocumentationPage.js');
describe("Testing the Docker Documentation UI", function(){
var hp = new HomePage();
var dp = new DocumentationPage();
beforeEach(function(){
browser.ignoreSynchronization = true;
dp.go();
});
it("Validate accordion navigation drills down", function(){
dp.drillDownToAmazonInstallDocumentation();
dp.clickInstallOnAmazonLink();
expect(browser.getTitle()).toContain("Amazon EC2 Installation");
});
it("Validate accordion navigation drills up", function(){
dp.drillDownToAmazonInstallDocumentation();
dp.drillUpToAmazonInstallDocumentation();
});
});

Related

Uncaught TypeError: Cannot read property 'style' of null / Oracle JET

Hello I'm a beginner in JS and Oracle JET Framework.
I'm trying to implement a Panel Expand/Collapse item in my project (http://www.oracle.com/webfolder/technetwork/jet-310/jetCookbook.html?component=animation&demo=panelExpand) and I have this error I don't know why I followed the cookbook. Here is my code :
My HTML :
<div id="animationDemo">
<div id="panel" class="oj-panel oj-margin basepanel">
<h3>Panel Expand/Collapse Demo</h3>
<div>Primary Content</div>
<div id="extra-content" class="oj-panel oj-panel-alt2 childpanel">Extra Content</div>
<button class="oj-panel-resize-button"
data-bind="click: buttonClick,
ojComponent: {component: 'ojButton',
chroming: 'half',
display: 'icons',
icons: buttonIcons,
label: buttonLabel}"></button>
</div>
</div>
My JS
define(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout',
'promise', 'ojs/ojtable', 'ojs/ojarraytabledatasource','ojs/ojbutton', 'ojs/ojanimation'],
function(oj, ko, $) {
function CustomerViewModel() {
var self = this;
////
var panel = document.getElementById('panel');
var extra = document.getElementById('extra-content');
var initHeight = $(panel).css('height');
// Keep track of whether the element is expanded
self.expanded = false;
self.buttonIcons = ko.observable({end:'oj-panel-expand-icon'});
self.buttonLabel = ko.observable('expand');
self.buttonClick = function() {
if (self.expanded) {
// Call the collapse method, then hide the extra content when animation ends.
oj.AnimationUtils['collapse'](panel, {'endMaxHeight': initHeight}).then(function() {
extra.style.display = 'none';
self.expanded = false;
self.buttonIcons({end:'oj-panel-expand-icon'});
self.buttonLabel('expand');
});
} else {
// Mark the extra content to be displayed, followed by a call to the expand method.
extra.style.display = 'block';
oj.AnimationUtils['expand'](panel, {'startMaxHeight': initHeight}).then(function() {
self.expanded = true;
self.buttonIcons({end:'oj-panel-collapse-icon'});
self.buttonLabel('collapse');
});
}
};
///
self.connected = function() {
// Implement if needed
};
self.disconnected = function() {
// Implement if needed
};
self.transitionCompleted = function() {
};
self.hello2 = function() {
alert('hhhh');
};
}
return new CustomerViewModel();
}
);
Thank you for your help
It's because the HTML hasn't loaded yet when it comes to these lines:
var panel = document.getElementById('panel');
var extra = document.getElementById('extra-content');
var initHeight = $(panel).css('height');
So their values become null.
Instead, you could call them only after the document has loaded using jQuery, like this:
var panel;
var extra;
var initHeight;
$(document).ready(function(){
panel = document.getElementById('panel');
extra = document.getElementById('extra-content');
initHeight = $(panel).css('height');
});
Or if you want to do it the pure OJET way, you could do this:
var panel;
var extra;
var initHeight;
self.handleBindingsApplied = function(){
panel = document.getElementById('panel');
extra = document.getElementById('extra-content');
initHeight = $(panel).css('height');
};
self.handleBindingsApplied is an internal method of an OJET Viewmodel that gets called automatically after the binding between the HTML and Viewmodel is complete.
You can read more about all the internal methods here.
P.S. don't forget to apply the CSS as well from the cookbook.

Button could not be tapped - UIAutomation

I am attempting to create script that will automate sign in process (to login). However, when I run script it says that 'could not be tapped'. Below is what I have written so far:
var target = UIATarget.localTarget();
var app = target.frontMostApp();
var appWindow = app.mainWindow();
var settingsButton = appWindow.buttons()["CloseVector"];
var settingsView = app.mainWindow().staticTexts()["Settings"]
UIALogger.logStart("Sign in test")
settingsButton.tap();
target.delay(1);
if (settingsView.isValid())
{
UIALogger.logPass("Correct View");
}
else
{
UIALogger.logFail("Wrong View");
}

Convert jQuery view handler to Angular Controller

Trying to convert a lot of jQuery and JS to an Angular controller so that it works the "NG" way. I've looked at the docs and it looks like I can setup a document ready function in Angular using something like angular.element(document).ready to initialize the jQuery variable. What I am confused on is "this" and it's place in the angular world. Anyone have an idea of where to start?
Here is a Pen of what I am trying to accomplish:
CodePen
And I know that there is ng-show/ng-hide and that ngAria shows up around 1.3 but I am just trying to grasp the basis for converting a lot of jQuery to angular.
Here is my current script:
$(document).ready(function() {
var hs1 = new hideShow('open1', 'close1');
var hs2 = new hideShow('open2', 'close2');
var hs3 = new hideShow('open3', 'close3');
var hs4 = new hideShow('open4', 'close4');
});
function hideShow(toggleID, closeID) {
this.$toggle = $('#' + toggleID);
this.$close = $('#' + closeID);
this.$region = $('#' + this.$toggle.attr('aria-controls'));
this.keys = {
enter: 13,
space: 32
};
this.toggleSpeed = 100;
this.bindHandlers();
}
hideShow.prototype.bindHandlers = function() {
var thisObj = this;
this.$toggle.click(function(e) {
thisObj.toggleRegion();
e.stopPropagation();
return false;
});
this.$close.click(function(e) {
thisObj.hideRegion();
e.stopPropagation();
return false;
});
}
hideShow.prototype.hideRegion = function() {
this.$region.hide().attr('aria-expanded', 'false');
this.$toggle.find('span').html('Show');
this.$toggle.focus();
}
hideShow.prototype.toggleRegion = function() {
var thisObj = this;
this.$region.slideToggle(this.toggleSpeed, function() {
if ($(this).attr('aria-expanded') == 'false') { // region is collapsed
$(this).attr('aria-expanded', 'true');
$(this).focus();
thisObj.$toggle.find('span').html('Hide');
} else {
$(this).attr('aria-expanded', 'false');
thisObj.$toggle.find('span').html('Show');
}
});
}
Any suggestions or examples are appreciated.

knockout dirty flag code not working

Just started with knockout and need to implement page change warning. Following is the code snippet. I just need an alert pop up as warning if any change is made on the page.
function parseViewModel() {
var viewModel = JSON.parse(getState());
viewModel.checking = ko.observable(false);
viewModel.Slider = new ko.observable(100 - viewModel.Slider);
viewModel.CausalsList = buildHierarchy(viewModel.Causals);
viewModel.Causals["-1"] = "Total Marketing Budget";
viewModel.GeographiesList = ko.observableArray(gl);
viewModel.Geographies["0"] = "All Geographies";
viewModel.ProductsList = ko.observableArray(pl);
viewModel.Products["0"] = "All Products";
.
.
.
return viewModel;
}
function bindModel() {
model = parseViewModel();
ko.dirtyFlag = function (root, isInitiallyDirty) {
var result = function () { },
_initialState = ko.observable(ko.toJSON(root)),
_isInitiallyDirty = ko.observable(isInitiallyDirty);
result.isDirty = ko.computed(function () {
return _isInitiallyDirty() || _initialState() !== ko.toJSON(root);
});
result.reset = function () {
_initialState(ko.toJSON(root));
_isInitiallyDirty(false);
};
return result;
};
model.dirtyFlag = new ko.dirtyFlag(model);
model.isDirty.subscribe(function () {
alert("Page change warning!");
});
ko.applyBindings(model, $('#const').get(0));
ko.applyBindings(model, $('#buttonDiv').get(0));
}
Referred Ryan Niemeyer's blog. Unfortunately, it's not working anymore. Any insights please?
You would want to subscribe to model.dirtyFlag.isDirty in your case rather than model.isDirty.
One way to do is by using customBinding. I'm not that familiar with KO either but this might be something you're interested on.
Basically you would do is :-
ko.bindingHandlers.myFunction = {
update : function(){
//do something
}
}
http://knockoutjs.com/documentation/custom-bindings.html
And call it on your element using :-
<h1 data-bind="myFunction:{}"></h1>
Also, a jsfiddle to show how it works. (If you change the value of the First Name and focus out of it then the customBinding gets triggered. )
http://jsfiddle.net/3vuTk
Not sure if it's the best practice though.

Change hash without triggering Sammy event

function UsersVM(start_page){
var self = this;
console.log('start form ' + start_page);
self.go_to = function(page) {
location.hash = '#Users/' + pageNumber;
}
}
Sammy(function() {
this.get('/app/?#Users/:page', function () {
var vm = new UsersVM(this.params.page);
ko.applyBinding(vm);
});
}).run();
I would like to change the page's hash with the following code:
location.hash = '#Users/' + pageNumber;
But in this case Sammy triggers routing. Say in Backbone we can do it this way:
app.navigate("help/troubleshooting", {trigger: false});
Is it possible to do it in Sammy?
Thanks!
I don't know of a native way to do this in Sammy, but here is a solution that has worked for me:
var sam = $.sammy(function () {
var sammy = this; //get a persistent reference to this
sammy.quiet = false; //set quiet to false by default
//I set quiet to true before running a route
sammy.quietRoute = function (location) {
sammy.quiet = true;
sammy.setLocation(location);
}
//I'm called after every route to reset quiet to false
sammy.after(function () {
sammy.quiet = false;
});
//I'm a 'normal' route that does not have the capability to be 'quiet'
this.get('#normalRoute', function () {
//routing code
});
//I am a route that can be 'quieted' so that when the url or
//hash changes my routing code doesn't run
this.get('#quietableRoute', function () {
if (!sammy.quiet) {
//routing code
} else {
return;
}
});
});
Then call the quietRoute function in your code:
//This will work
sam.quietRoute("#quietableRoute");
//This will not work because the "if(!sammy.quiet)..." code has not been
//implemented on this route
sam.quietRoute("#normalRoute");
Use the following code:
var new_location = '#foo';
app.trigger('redirect', {to: new_location});
app.last_location = ['get', new_location];
app.setLocation(new_location);

Categories