I have a web site that I'm testing via Jasmine. My tests are spread across multiple JavaScript files. For that reason, I would like to have a JavaScript object that I can share across my tests. For example, I'd like to have the following JSON be available to all of the tests in my files.
var settings = {
rootUrl: 'http://www.example.com',
username: 'test'
};
Then, in my tests, I'd like to do something like the following:
tests1.js
describe('TestSet1', function() {
it('Should load properly', function(done) {
var url = settings.rootUrl + '/contact';
// do stuff
});
});
tests2.js
describe('TestSet2', function() {
it('Should load properly', function(done) {
var url = settings.rootUrl + '/login';
// do stuff
});
});
How do I get settings to be available across JavaScript test files? Where do I define settings? How do I import settings into my tests?
Thank you
If this is node, you can require it in at the top of all the pages:
var settings = require('./settings.json');
If browser, you should be able to add a dependency above the test adding it to the global scope.
window.settings = {};
In node.js you would var settings = require('./settings'); at the beginning of your tests. Export the settings variable in the settings file:
var settings = {
rootUrl: 'http://www.example.com',
username: 'test'
};
module.exports = settings;
In browser you might just want to include the file with a script tag before you include your specs:
<!-- include spec files here... -->
<script src="spec/settings.js"></script>
<script src="spec/test1.js"></script>
<script src="spec/test2.js"></script>
Related
I have a simple javascript module pattern that executes client-side.
var module = (function() {
var _privateVariable = 10;
function publicMethod () {
console.log("public method; private variable: " + _privateVariable);
}
return {
publicMethod: publicMethod
};
})();
Let's say I want to load in another module (which also uses a module pattern) from a separate javascript file. How do I do that, i.e. something like:
?? Load other module here ??
var _other_module = ??
var module = (function() {
var _privateVariable = 10;
function publicMethod () {
console.log("public method; private variable: " + _privateVariable);
console.log("public method; other module: " + other_module.publicMethod());
}
return {
publicMethod: publicMethod
};
})();
You can't. To load another module form another file you need to use a Module formats.
It's a long story, i will try to shorten.
Let's talk first about the old way. earlier the developer used to load alle the JS-Files in a certain order in the HTML-Page. If we have 2 JS-Files index.js and variables.js and we want to get a variable from varible.js in index.js, we had load them like that
<script src="variables.js"></script>
<script src="index.js"></script>
But this is not a good way and has many negatives.
The right way is to use a Module formats.
There are many Module formats,
Asynchronous Module Definition (AMD)
CommonJS
Universal Module Definition (UMD)
System.register
ES6 module format
and each format has own Syntax.
For example CommonJS:
var dep1 = require('./dep1');
module.exports = function(){
// ...
}
but the Browsers dont't understand that. so we need a Module bundlers or Module loaders
to convert our code to code which browsers can understand.
Module bundlers: Bundle your inter-dependent Javascript files in the correct order
Browserify
Webpack
Module loaders: a module loader interprets and loads a module written in a certain module format.
RequireJS
SystemJS
This article will help you so much to understand exactly how modules work.
Not sure which context you are doing this.
But. In node.JS this would be typically done by
module1.js
module.exports.publicMethod = function() {}
index.js
const module1 = require('./module1.js');
module1.publicMethod();
or
const {publicMethod} = require('./module1.js');
publicMethod();
We have a rather big set of end-to-end tests on Protractor. We are following the Page Object pattern which helps us to keep our tests clean and modular. We also have a set of helper functions which help us to follow the DRY principle.
The Problem:
A single spec may require multiple page objects and helper modules. For instance:
"use strict";
var helpers = require("./../../helpers/helpers.js");
var localStoragePage = require("./../../helpers/localStorage.js");
var sessionStoragePage = require("./../../helpers/sessionStorage.js");
var loginPage = require("./../../po/login.po.js");
var headerPage = require("./../../po/header.po.js");
var queuePage = require("./../../po/queue.po.js");
describe("Login functionality", function () {
beforeEach(function () {
browser.get("/#login");
localStoragePage.clear();
});
// ...
});
You can see that we have that directory traversal in every require statement: ./../... This is because we have a specs directory where we keep the specs and multiple directories inside grouped by application functionality under test.
The Question:
What is the canonical way to approach the relative path problem in Protractor?
In other words, we'd like to avoid traversing the tree, going up to import modules. It would be much cleaner to go down from the base application directory instead.
Attempts and thoughts:
There is a great article about approaching this problem: Better local require() paths for Node.js, but I'm not sure which of the options is a recommended one when developing tests with Protractor.
We've also tried to use require.main to construct the path, but it points to the node_modules/protractor directory instead of our application directory.
I had the same problem and I ended up with the following solution.
In my Protractor config file I have a variable which stores a path to a base folder of my e2e tests. Also, Protractor config provides the onPrepare callback, where you can use a variable called global to create global variables for your tests. You define them as a properties of that global variable and use the same way you use globals browser or element in tests. I've used it to create custom global require functions to load different types of entities:
// __dirname retuns a path of this particular config file
// assuming that protractor.conf.js is in the root of the project
var basePath = __dirname + '/test/e2e/';
// /path/to/project/test/e2e/
exports.config = {
onPrepare: function () {
// "relativePath" - path, relative to "basePath" variable
// If your entity files have suffixes - you can also keep them here
// not to mention them in test files every time
global.requirePO = function (relativePath) {
return require(basePath + 'po/' + relativePath + '.po.js');
};
global.requireHelper = function (relativePath) {
return require(basePath + 'helpers/' + relativePath + '.js');
};
}
};
And then you can use these global utility methods in your test files right away:
"use strict";
var localStorageHelper = requireHelper('localStorage');
// /path/to/project/test/e2e/helpers/localStorage.js
var loginPage = requirePO('login');
// /path/to/project/test/e2e/po/login.po.js
var productShowPage = requirePO('product/show');
// /path/to/project/test/e2e/po/product/show.po.js
describe("Login functionality", function () {
beforeEach(function () {
browser.get("/#login");
localStorageHelper.clear();
});
// ...
});
We've been facing the same issue and decided to turn all page object and helper files into node packages. Requiring them in tests is now as easy as var Header = require('header-po'). Another benefit of converting to packages is that you can use proper versioning.
Here is a simple example:
./page-objects/header-po/index.js
//page-objects/header-po/index.js
'use strict';
var Header = function () {
this.goHome = function () {
$('#logo a').click();
};
};
module.exports = Header;
./page-objects/header-po/package.json
{
"name": "header-po",
"version": "0.1.1",
"description": "Header page object",
"main": "index.js",
"dependencies": {}
}
./package.json
{
"name": "e2e-test-framework",
"version": "0.1.0",
"description": "Test framework",
"dependencies": {
"jasmine": "^2.1.1",
"header-po": "./page-objects/header-po/",
}
}
./tests/header-test.js
'use strict';
var Header = require('header-po');
var header = new Header();
describe('Header Test', function () {
it('clicking logo in header bar should open homepage', function () {
browser.get(browser.baseUrl + '/testpage');
header.goHome();
expect(browser.getCurrentUrl()).toBe(browser.baseUrl);
});
});
I have had the same issue. Did similar solution to Michael Radionov's, but not setting a global function, but setting a property to protractor itself.
protractor.conf.js
onPrepare: function() {
protractor.basePath = __dirname;
}
test-e2e.js
require(protractor.basePath+'/helpers.js');
describe('test', function() {
.......
});
I think the method we use where I work might be a good solution for you. I have posted a brief example of how we handle everything. It's pretty nice b/c you can just call the page object functions in any spec file and you don't need to use require in the spec.
Call a node module from another module without using require() everywhere
All answers seem to be more of workarounds
The actual working solution would be this:
install module alias
add this to your package.json
"_moduleAliases": {
"#protractor": "protractor/_protractor",
"#tmp": "protractor/.tmp_files",
"#test_data": "protractor/.tmp_files/test_data",
"#custom_implementation": "protractor/custom_implementation",
},
add this as very first line of your protractor config
require('module-alias/register');
use it anywhere in the project like so
const params = require('#test_data/parameters');
const customImplementation require('#custom_implementation')
// etc
I'm not sure if I'm understanding Browserify correctly, but am I able to essentially require() this smooth-scroll plugin (installed via npm) in my app.js file and Browserify will bundle it all together in my final app.js file?
app.js
var ss = require('./smooth-scroll');
$(document).ready(function(){
ss.init();
});
gulpfile.js
gulp.task('js', function () {
var browserified = transform(function(filename) {
var b = browserify(filename);
return b.bundle();
});
return gulp.src('./src/js/app.js')
.pipe(browserified)
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./_site/public/js'))
});
Update
So, I'm trying to create my own module using the answer below but I'm having a problem in getting it to work with jquery.adaptive-backgrounds.js.
adaptive-backgrounds.js
var $ = require('jquery/dist/jquery');
module.exports = function(){
/* jshint debug: true, expr: true */
;(function($){
/* Constants & defaults. */
var DATA_COLOR = 'data-ab-color';
var DATA_PARENT = 'data-ab-parent';
var DATA_CSS_BG = 'data-ab-css-background';
var EVENT_CF = 'ab-color-found';
...
};
app.js
var $ = require('jquery/dist/jquery');
var ab = require('./vendors/adaptive-backgrounds');
$(function(){
$.ab.run();
});
For some reason the adaptive backgrounds plugin doesn't seem to be running on the page.
That is correct, if Smooth Scroll is available as a npm module. If it´s not you can create your own module with the script. Then, browserify would bundle everything into one js file.
Or you could just add it to your web page before your bundle, as you would normally do, and then it would be accessible from the bundle since it would be declared in global scope. But in this case, keep in mind you should concatenate your scripts for production (in the right order) to reduce the page loading time by minimizing the number of http requests.
Creating your own module is really simple. Let's say Smooth Scroll wasn't available as npm module, you could simply wrap the plugin in an anonymous function and assign that function to module.exports and then call the related variable in your bundle when you require the modified plugin.
Your plugin would then look like this:
/*!
* jQuery Smooth Scroll - v1.5.2 - 2014-10-01
* https://github.com/kswedberg/jquery-smooth-scroll
* Copyright (c) 2014 Karl Swedberg
* Licensed MIT (https://github.com/kswedberg/jquery-smooth-scroll/blob/master/LICENSE-MIT)
*/
//WE ADD THIS, BECAUSE ITS A DEPENDENCY (downloaded with npm)
var jQuery = require('jquery');
//AND THIS
module.exports = function(){
//THIS IS THE ORIGINAL CODE
(function($) {
var version = '1.5.2',
optionOverrides = {},
defaults = {
exclude: [],
excludeWithin:[],
offset: 0,
//....
//AND WE CLOSE OUR FUNCTION
};
And in the bundle you would do something like this
var SmoothScroll = require('../jquery.smooth-scroll');
var $ = require('jquery');
SmoothScroll();
// From now on Smooth Scroll is available
$('#mydiv').smoothScroll();
Something to note: you could have simply assigned the existing immediate anonymous function wrapping the plugin code to module.exports and then simply calling require('./jquery.smooth-scroll') would have make it available in the current scope. But imo it's better practice to wrap it with another anonymous function to do a call to make the plugin available to current scope explicitly.
Update
For your plugin, you should first do
npm install jquery. Because jquery as a regular script isn't exported as a module. So you need to install jquery module (or you could do the export yourself but why reinventing the wheel?)
Then...
adaptive-backgrounds.js
//Here you have to call your variable jQuery since its named this way when
//it's passed as an argument to the plugin immediate function
var jQuery = require('jquery');
module.exports = function(){
/* jshint debug: true, expr: true */
(function($){
/* Constants & defaults. */
var DATA_COLOR = 'data-ab-color';
var DATA_PARENT = 'data-ab-parent';
var DATA_CSS_BG = 'data-ab-css-background';
var EVENT_CF = 'ab-color-found';
...
// This is what I meant
})(jQuery);
};
app.js
//Now you can use $
var $ = require('jquery');
var ab = require('./vendors/adaptive-backgrounds');
//add this to call the immediate function used to set up the plugin
ab();
$(function(){
$.ab.run();
});
I am aware I can create a custom file inside the config directory and reference the variables from within that
module.exports.myconfig = {
foo: 'bar'
}
sails.config.myconfig.foo
But I need to write to these variables too and have them saved. In previous projects I have done this with JSON config files and used PHP to write to them.
Is there any way of doing this with Sails or should I just create some JSON files to pull and push my config vars?
There's no mechanism built in to Sails for persisting configuration variables. However, in the latest build of Sails there is a lower event you can listen for which indicates that Sails is exiting. You could catch this and persist your data then. For example, in your /config/bootstrap.js, something like:
var fs = require('fs');
module.exports = function(cb) {
sails.on('lower', function persistConfig() {
fs.writeFileSync(sails.appPath+'/config/myConfig.js',
'module.exports = ' + JSON.stringify(sails.config.myconfig));
});
// ... other bootstrap stuff ...
return cb();
}
When using JUnit and Maven in Java, one can have separate property files for src/main and src/test. This allows different configuration for code and tests, having Maven to manage the resources by using Java classpath.
Is there a similar way in Javascript code run by Node.js? I use Mocha for unit-testing and Grunt for task management.
Code example for script.js:
var config = require('./config/dev/app.js');
exports.getFileName = function() {
return config.fileName; // returns 'code.txt'
}
What I need is to make the script.js use different config file when being required in a test.js unit test like this:
var assert = require('assert');
var s = require('./script.js');
describe('Test', function () {
it('should use different config file', function() {
assert.equal('test.txt', s.getFileName());
});
});
Is there a way to use different configuration ./config/test/app.js in the script.js without having to alter the code of script.js? What I really try to avoid is to adjust the code to support unit tests. Instead, I want to achieve similar functionality such as mentioned Java classpath.
Please try this code.
Script.js
var config;
if(process.env.dev===true){
config = require('./config/dev/config.js');
}
if(process.env.prod===true){
config = require('./config/prod/config.js');
}
exports.getFileName = function() {
return config.fileName; // returns 'code.txt'
}
test.js
//set the environment here
process.env.dev = true;
var assert = require('assert');
var s = require('./script.js');
describe('Test', function () {
it('should use different config file', function() {
assert.equal('test.txt', s.getFileName());
});
});
I have not found any elegant solution out there on the web so I have implemented and published my own.
Check it out here: https://npmjs.org/package/app-config
Using the app-config plugin, only the script.js needs to get changed this way:
var config = require('app-config').app;
exports.getFileName = function() {
return config.fileName; // returns 'code.txt'
}
The app needs to be run this way for example
NODE_ENV=dev node script.js
NODE_ENV=unitTest mocha test.js
Depending on the NODE_ENV environmental variable, the right set of configuration files will be loaded by the app-config plugin.