Using require with relative paths

22,669

Solution 1

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();
    });

    // ...

});

Solution 2

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);
    });
});

Solution 3

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() {
   .......
});

Solution 4

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

Solution 5

All answers seem to be more of workarounds

The actual working solution would be this:

    "_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
Share:
22,669
alecxe
Author by

alecxe

"I am a soldier, at war with entropy itself" I am a Software Developer and generalist who is in love with the Python language and community. I greatly value clean and maintainable code, great software, but I know when I need to be a perfectionist and when it stands in a way of product delivery. I like to break things, to find new ways to break things, to solve hard problems, to put things under test and stress, and to have my mind blown by an interesting question. Some of my interests: Learning, Productivity, AI, Space Exploration, Internet of Things. "If you change the way you look at things, the things you look at change." - Wayne Dyer If you are looking for a different way to say "Thank you": Amazon wish list Pragmatic wish list Today I left my phone at home And went down to the sea. The sand was soft, the ocean glass, But I was still just me. Then pelicans in threes and fours, Glided by like dinosaurs, An otter basked upon its back, And dived to find another snack. The sun corpuscular and bright, Cast down a piercing shaft, And conjured an inspiring sight On glinting, bobbing craft. Two mermaids rose up from the reef, Out of the breaking waves. Their siren song was opium grief, Their faces from the grave. The mermaids asked a princely kiss To free them from their spell. I said to try a poet’s bliss. They shrugged and bid farewell. The sun grew dark and sinister, In unscheduled eclipse. As two eight-headed aliens Descended in their ships. They said the World was nice enough But didn’t like our star. And asked the way to Betelgeuse, If it wouldn’t be too far. Two whales breached far out to sea, And flew up to the sky, The crowd was busy frolicking, And didn’t ask me why. Today I left my phone at home, On the worst day, you’ll agree. If only I had pictures, If only you could see. Not everything was really there, I’m happy to confess, But I still have the memories, Worth more than tweets and stress. Today I left my phone at home, I had no shakes or sorrow. If that is what my mind can do, It stays at home tomorrow. Gavin Miller

Updated on July 14, 2022

Comments

  • alecxe
    alecxe almost 2 years

    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.

  • Sergey Pleshakov
    Sergey Pleshakov over 3 years
    I hope I didn't miss anything important, since I implemented this 2 years ago and still use it without modification. Let me know