What is the right way to handle the Page Objects model for writing Protractor E2E testing for AngularJS? I feel like I should write them in separate files (like homepage.js, page2.js, etc) then include somehow them into the specs like modules as need. However, I don't know how to inject them. Would appreciate any suggestions. Thanks!
Keep them in separate files, and use Node's require to pull in any helpers or page objects you need. Check out this super simple example: https://github.com/juliemr/ng-page-e2e/blob/master/test/angularsite_test.js#L2
Have you tried with astrolabe? It's a PageObject implementation on top of Protractor that could guide your structure: https://github.com/stuplum/astrolabe
There you can see that the recommended structure is one page object per file.
E.g.: singInPage.js
var Page = require('astrolabe').Page;
module.exports = Page.create({
url: { value: 'http://<somesite>.com/signin' },
username: { get: function() { return this.findElement(this.by.input('username')); } }, // finds an input element with the name 'username'
submit: { get: function() { return this.findElement(this.by.id('submit')); } } // finds an element with the id 'submit'
});
it makes the writing of test cases and even page object very compact and readable.
You should keep them in separate files, yes.
And in your protractor referenceConf.js (config to launch protractor with) you should write:
specs: ['<your_path>/test/pages/*Test.js']
In this case< protractor will launch all files from dir "/test/pages" with mask *Test.js (loginPageTest.js, homePageTest.js)
I'm afraid that there's no common standards when it comes to testing with page objects.
You could find several proposals among protractor's issues: https://github.com/angular/protractor/issues/401 and https://github.com/angular/protractor/issues/78
For my needs I created very simple page objects in my open source project, see: https://github.com/9ci/angle-grinder/pull/124
Also quite interesting implementation you could find in https://github.com/juliemr/ng-page-e2e/tree/master/test
Related
We have an UI development library consisting of widgets built on top of Angular JS. Let's pick "header-widget" as an example - a header container built with Angular JS, but with specific functionality on top if it.
Say the HTML code of this widget will look like this:
<header class="my-header">
<div ng-if="configuration.hasTitle">
<h1 class="my-header-title-text">
<span class="my-header-title-text">This is a main title</span>
<span class="my-header-subtitle-text">This is a subtitle</span>
</h1>
</div>
</header>
I can create an unit/integration test using Karma i.e. at one point I will have something like this:
element = angular.element("<header config=\"config\"></header>");
$compile(element)(myScope);
myScope.$digest();
expect(element.find(".my-header-title-text").text().trim()).toBe("This is a main title");
and I can create an end to end test using Protractor i.e. at one point I will have something like this:
describe('Some end to end spec', function () {
it('should have the right title', function () {
let title = element(by.css(".my-header .my-header-title-text"));
expect(title.getText()).toMatch("This is a main title");
});
});
Suppose the developers change the header and the new class for the title becomes "my-header-MAIN-title-text". I would have to go to all places in unit/integration tests as well as in the end to end tests to fix that.
(For the sake of this example, the change is trivial and in theory can be encapsulated by having selectors in separate files, but in practice I speak about bigger changes in the DOM structure of these widgets, etc.)
I want to build a test library - like a test API - on top of the widgets. An encapsulation layer that would offer me things like header.getTitle() and header.getSubtitle() - that I can use in both unit/integration (Karma) as well as in the end to end world (Protractor). This way, if a change like the one mentioned above will occur, I have to go in one place, fix the implementation of the "header.getTitle" and none of the tests should care about.
To do so, I have created something like this (using TypeScript):
import $ from "jquery";
export class MyHeaderImpl implements MyHeadear {
private selector;
constructor(element) {
this.selector = $(element);
}
getTitle(): string {
return this.selector.find(".my-header-title-text").text().trim();
}
getSubtitle(): string {
return this.selector.find(".my-header-subtitle-text").text().trim();
}
}
It works fine for my unit/integration tests (Karma):
element = angular.element("<header config=\"config\"></header>");
$compile(element)(myScope);
myScope.$digest();
const header = ComponentFactory.createHeader(element);
expect(header.getTitle()).toBe("This is a main title");
However, it will not work on Protractor:
describe('Some end to end spec', function () {
it('should have the right title', function () {
let element = element(by.css(".my-header .my-header-title-text"));
const header = ComponentFactory.createHeader(element);
expect(title.getText()).toMatch("This is a main title");
});
});
It throws the following exception
Failed: jquery_1.default is not a function
I am using TypeScript in the test library, as well as with my Protractor tests.
I can replace the jQuery $ with Protractor's element in my test library, but then I would force the unit/integration tests to run against a browser, which will defeat their purpose of being fast.
Does anyone knows how can I create such a test API library that can be used both by Karma tests, as well as by Protractor tests?
One of the best descriptions I could find here on Stack Overflow, but it does not answer my question.
Have you tried to inject the library in karma and use it directly without importing. There are tons of karma adapters so for jquery I think. In protractor you could do the same in protractor config. The test API you described is called Page Object pattern BTW, thou I'm not sure if it's a good idea to mix both type of tests in single spec.
#factor5, here is my answer to your suggestion, as the comment section doesn't allow it.
First, let me summarize my question like this:
is there a way to write (TypeScript) a method that accepts both:
Angular element (https://docs.angularjs.org/api/ng/function/angular.element)
Protractor element (https://www.protractortest.org/#/api?view=ElementFinder)
and then looks inside given element, finds a div, and returns its text?
If I define this element argument as the Angular one, I cannot reuse it in the Protractor end to end project.
If I define this element argument as "import {element} from protractor", it will force my unit/integration tests to be actually end to end tests, going through the WebDriver.
Second, about Page Object pattern: I go one level below Page Object pattern. Let's take the comment text area, where I type my answer right now.
<div class="js-comment-text-input-container">
<textarea></textarea>
</div>
I might have a Page Object called QuestionPage,with a method typeComment(), or even have a separate component (PageObject) called CommentSection, where I place methods like typeComment(), sendComment(), editComment(), etc.
But the might be used in many other places, i.e. in a page where user can
can add a short bio about, etc. There will be a Page Object called UserProfile, which has a typeBio() that inside also deals with
.
At this point, if the structure of changes, you already have two places where you have to
go and change.
Therefore, I need a separate library to isolate the interaction with such elements.
So I'm working with an enterprise tool where we have javascript scripts embedded throughout. These scripts have access to certain built-in objects.
Unfortunately, the tool doesn't give any good way to unit test these scripts. So my thinking was to maintain the scripts in a repo, mock the built-in objects, and then set up unit tests that run on my system.
I'm pretty ignorant to how JavaScript works in terms of building, class loading, etc. but I've been just trying things and seeing what works. I started by trying out Mocha by making it a node project (even though it's just a directory full of scripts, not a real node project). The default test works, but when I try and test functions from my code, I get compiler errors.
Here's what a sample script from my project looks like. I'm hoping to test the functions, not the entire script:
var thing = builtInObject.foo();
doStuff(thing);
doMoreStuff(thing);
function doStuff(thing) {
// Code
}
function doMoreStuff(thing) {
// More Code
}
Here's what a test file looks like:
var assert = require('assert');
var sampleScript = require('../scripts/sampleScript.js');
describe('SampleScript', function() {
describe('#doStuff()', function() {
it('should do stuff', function() {
assert.equal(-1, sampleScript.doStuff("input"));
});
});
});
Problem happens when I import ("require") the script. I get compilation errors, because it doesn't builtInObject. Is there any way I can "inject" those built in objects with mocks? So I define variables and functions that those objects contain, and the compiler knows what they are?
I'm open to alternative frameworks or ideas. Sorry for my ignorance, I'm not really a javascript guy. And I know this is a bit hacky, but it seems like the best option since I'm not getting out of the enterprise tool.
So if I get it right you want to do the unit tests for the frontened file in the Node.js environment.
There are some complications.
First, in terms of Node.js each file has it's own scope so the variables defined inside of the file won't be accessible even if you required the file. So you need to export the vars to use them.
module.exports.doStuff = doStuff; //in the end of sample script
Second, you you start using things like require/module.exports on the frontend they'll be undefined so you'll get an error.
The easiest way to run your code would be. Inside the sample script:
var isNode = typeof module !== 'undefined' && module.exports;
if (isNode) {
//So we are exporting only when we are running in Node env.
//After this doStuff and doMoreStuff will be avail. in the test
module.exports.doStuff = doStuff;
module.exports.doMoreStuff = doMoreStuff;
}
What for the builtInObject. The easies way to mock it would be inside the test before the require do the following:
global.builtInObject = {
foo: function () { return 'thing'; }
};
The test just passed for me. See the sources.
Global variables are not good anyway. But in this case seems you cannot avoid using them.
Or you can avoid using Node.js by configuring something like Karma. It physically launches browser and runs the tests in it. :)
I have a interesting concept I was working on and looking over, through various stack questions on auto loading JavaScript. I dint want to use a third party tool, aside form jquery, so I thought I would role my own. The concept I have is:
var scripts = {
'name' : 'path/to/script_dir/' // Load all scripts in this file.
}
requireScripts(scripts); // Requires all scripts
// Call your classes, methods, objects and so on ....
The requireScript() function would work something like:
function requireScript(hash){
$.each(hash, function(key, value)){
$.ajax({
url: value,
dataType: "script",
async: false,
error: function () {
throw new Error("Could not load script " + script);
}
});
});
}
Note: The above is just a concept, I don't think it will work.
The above would let you load SPECIFIC scripts. so in essence your hash key value would be 'name' : 'path/to/specific/script'. The issue this posses is that your hash would get rather large ....
The other issue I ran into is what if I simplified this to "php pear naming standard" so, as the trend seems to be - we would create a class, and it would be named after its location:
var some_folder_name_class = function(){}
Would be translated by the autoloader as: some/folder/name/class.js and then loaded that way.
To wrap up and get to my point there are two ways of loading javascript file I am looking at, via rolling my own "require" method. One is loading a directory of javascript files via the hash idea implemented above. (the provided code sample of how this hash would be walked through would have to be changed and fixed....I dont think it works to even load a single file)
OR
to have you just do:
new some_class_name() and have a global function listen for the new word, go find the file your trying to call based on the name of the class and load it, this you never have to worry - as long as you follow "pear naming standards" in both class and folder structure your js file will be loaded.
Can either approach be done? or am I dreaming to big?
I see a lot of frameworks do a bunch of require('/path/to/script') and if I could role my own autoloader to just allow me to either load a directory of js files or even have it where it listens for new before a class instantiation then I could make my life SO MUCH easier.
Have you consider using requirejs and probably Lazy loading.
http://www.joezimjs.com/javascript/lazy-loading-javascript-with-requirejs/
Here is sample version:
You can download here.
The sample is based on this folder structure :
public
index.html
scripts
app.js
lib
** jquery-1.10.2.js
** require.js
3 . From Code:
html
`<!DOCTYPE html><html>
<head><title>Sample Test</title>`
<script src="scripts/lib/require.js"></script> <!-- downloaded from link provide above-->
<script src="scripts/app.js"></script></head>
`<body><h1>My Sample Project</h1><div id="someDiv"></div></body></html>`
application configuration app.js
requirejs.config({
baseUrl: 'scripts',
paths: {
app: 'app',
jquery: 'lib/jquery-1.10.2' //your libraries/modules definitions
}
});
// Start the main app logic. loading jquery module
require(['jquery'], function ($) {
$(document).on('ready',function(){
$('#someDiv').html('Hello World');
});
});
jQuery-only option
If you are looking for a jQuery-only solution, have a look at jQuery.getScript(). It would be a great candidate for handling the script loading portion of your problem. You could then write a very small wrapper around it to load all the scripts—something like you wrote above:
var loadScripts = function(scripts) {
$.each(scripts, function(name, path) {
jQuery.getScript("/root/path/" + path + ".js");
})
}
If you are interested in more information on this approach, read this article by David Walsh.
Other great libraries
I strongly recommend taking a look at the current batch of script-loading libraries. I think that you will pleasantly surprised by what is out there. Plus, they come with the benefit of great community support and documentation. RequireJS seems to be the front runner but David Walsh has great articles on curl.js and LABjs.
So I just need a sanity check on the way in which I layout my code for an application. I'm always keen to learn better approaches.
I basically use an Object Literal to organise my code meaning that I have one single global variable. Then for each section of the application I create a separate object - something like:
var MYAPP = {
init : function() {
//site wide common js
},
sections : {
homepage : function() {
//homepage js
},
anotherpage : function() {
//another page js
tools.usefultool();
}
},
tools : {
usefultool : function() {
//useful reuseable method
}
}
};
My question is while this helps code organisation, I'm wondering about objects being initialised but never used. For example - if I'm on a site's homepage I'll just call MYAPP.sections.homepage() . I don't actually need any of the other objects so I'm wondering - does this structure have a performance implication? Is there a better way? The structure closely follows the the great Rebecca Murphy article "Using Object to Organise Your Code" (http://blog.rebeccamurphey.com/2009/10/15/using-objects-to-organize-your-code).
Thanks!
Yes, there's always a performance hit in unused code as the parser has to actually interpret the code even if it's not executed. But any performance hit here is so minute that you're never going to notice it. The only real hit in unused code like this is in the bandwidth required to download it. If you have a 100kb file downloaded that you never use then you're wasting the time to download that file.
Iam trying to get better in javascript coding. Away from 1000 lines of code in one file. But iam not sure if this is the "right" way:
RequireJS to load files when needed inside "boot.js":
require([
"library/jquery.form/jquery.form",
"app/eventManager",
"app/myapp"
], function() {
$(function() {
MyApp.init();
});
});
MyApp.js
var MyApp = {
init: function() {
MyApp.mainController();
},
// this is our controller, only load stuff needed..
mainController: function() {
//controller = set from php/zendframework
switch (controller) {
case 'admin':
MyApp.initAdmin();
break;
default:
break;
}
},
// action for admin controller
initAdmin: function() {
//lazy load
require(["app/admin/admin"], function(){
MyApp.admin.init();
});
}};
MyApp.admin.js
MyApp.admin = {
init : function() {
if (permisson != 'admin') {
console.log('Permission denied.');
return false;
}
MyApp.admin.dashboard.init();
}
};
MyApp.admin.dashboard = {
init: function() {
MyApp.admin.dashboard.connectEventHandlers();
MyApp.admin.dashboard.connectEvents();
MyApp.admin.dashboard.getUserList('#admin-user-list');
},
connectEvents: function () {
EventManager.subscribe("doClearCache", function() {
MyApp.admin.dashboard.doClearCache(url);
});
EventManager.subscribe("doDeleteUser", function() {
MyApp.admin.dashboard.doDeleteUser(url);
});
},
What other "styles" are common? or this a goodway to structure code? THere a lot of examples in the net, but i was not able to find "real life" code..
And one of biggest "problems" when does i need ".prototype" ?
JavaScript Patterns is a good reference for various ways of structuring code.
It would also be a good idea to study the source code of libraries such as jQuery.
One change I would make to your code is to avoid repeating 'event' strings everywhere.
You could reduce this by doing something like:
var app = {
events : {
someEvent : "someEvent"
}
}
EventManager.subscribe(app.events.someEvent, someFn);
EventManager.publish(app.events.someEvent);
I would also avoid calling console.log directly and use a wrapper such as this which provides a fallback if not console is available
N.B Angus Croll has a decent blog where he mentions js structure/namespacing etc
and there is some really good knowledge being shared over at JsMentors from well versed js ninjas
I defer to Douglass Crockford on all matters pertaining to JavaScript best practices.
Here is his homepage: http://javascript.crockford.com/.
Here is a great book on what to do and what not to do in JavaScript. http://www.amazon.com/exec/obidos/ASIN/0596517742/wrrrldwideweb
Here is his amazing tool which can automatically tell you if you are employing any worst practices. http://www.jslint.com/
As to the prototype question, you use prototype when you want to employ prototypal inheritance, or create a "static" class function which will be present for all instances of that class without consuming memory for each instance.
Require.js is great tool, you can use it also on the client side. But be careful when you use it on mobile. In such case you should either use the editor to navigate better in one file or use thing like sprocket. It is a "precompiler", does not put any additional library to your code.
I passed through your sliced up code. Probably you should define the different parts as modules, read the requirejs documentation for defining modules, it gives good assistance.
But think twice whether you really need for organizing your code an extra library.
In case you are building something more complex, for example with multiple product modules and sub modules, I recommend creating a context hierachy for your modules. Also make the UI components to be self-contained so that you have templates, css, logic, assets, localization etc for a particular UI component in a single place.
If you need to refer a reference architecture for large scale js development see http://boilerplatejs.org. I'm the main author of it and it demonstrates a lot of patterns that are useful in complex product development.