Search code examples
angularjsfirefoxtestingprotractorend-to-end

Multiple firefox profiles in protractor


Following Configuring multiple capabilities with promises topic.

Use case: I have two separate tests that require Firefox to be fired with javascript disabled and local storage disabled. Which means that I need two firefox profiles with javascript.enabled = false and dom.storage.enabled = false desired capabilities/preferences set.

I'm using getMultiCapabilities() that was introduced in protractor 1.6. Till this moment, I needed only one custom firefox profile and it worked, here is the configuration:

getMultiCapabilities: function() {
    var deferred = q.defer();

    var multiCapabilities = [
        {
            browserName: "chrome",
            specs: [
                "*.spec.js"
            ],
            exclude: [
                "footer.disabledJavascript.spec.js"
            ]
        }
    ];

    // Wait for a server to be ready or get capabilities asynchronously.
    setTimeout(function() {
        var firefoxProfile = new FirefoxProfile();
        firefoxProfile.setPreference("javascript.enabled", false);
        firefoxProfile.encoded(function (encodedProfile) {
            var capabilities = {
                "browserName": "firefox",
                "firefox_profile": encodedProfile,
                "specs": [
                    "footer.disabledJavascript.spec.js"
                ]
            };
            multiCapabilities.push(capabilities);
            deferred.resolve(multiCapabilities);
        });
    }, 1000);

    return deferred.promise;
},

Problem: Now I need the second firefox profile instance with dom.storage.enabled = false, but I'm stuck at how should I resolve the deferred in this case, since there are now two encoded() calls and two capabilities added to multiCapabilities.

Question: How can I configure multiple firefox profiles using getMultiCapabilities?


The only way I made it work is nesting one profile into another and calling resolve() at the deepest level (in case of two profiles it might be okay - but this solution doesn't really scale):

var multiCapabilities = [
        {
            browserName: "chrome",
            specs: [
                "*.spec.js"
            ],
            exclude: [
                "footer.disabledJavascript.spec.js",
                "disabledLocalStorage.spec.js"
            ]
        }
    ];

    // Wait for a server to be ready or get capabilities asynchronously.
    setTimeout(function() {
        // profile with disabled javascript
        var firefoxProfile = new FirefoxProfile();
        firefoxProfile.setPreference("javascript.enabled", false);
        firefoxProfile.encoded(function (encodedProfile) {
            var capabilities = {
                browserName: "firefox",
                directConnect: true,
                firefox_profile: encodedProfile,
                specs: [
                    "footer.disabledJavascript.spec.js"
                ]
            };
            multiCapabilities.push(capabilities);

            // profile with disabled local storage
            var newFirefoxProfile = new FirefoxProfile();
            newFirefoxProfile.setPreference("dom.storage.enabled", false);
            newFirefoxProfile.encoded(function (newEncodedProfile) {
                var newCapabilities = {
                    browserName: "firefox",
                    directConnect: true,
                    firefox_profile: newEncodedProfile,
                    specs: [
                        "disabledLocalStorage.spec.js"
                    ]
                };
                multiCapabilities.push(newCapabilities);
                deferred.resolve(multiCapabilities);
            });
        });
    }, 1000);

    return deferred.promise;
},

Solution

  • You can use q.all to do this. Essentially you want to do something like this:

    return q.all([
        capabilityPromise1,
        capabilityPromise2,
        ...
    ]);
    

    Exactly how you get the capability promises is up to you. Here's one generic way:

    var makeFirefoxProfile = function(preferenceMap, capabilityMap) {
        var deferred = q.defer();
        var firefoxProfile = new FirefoxProfile();
        // TODO: iterate over preferenceMap and set preference for each
        firefoxProfile.encoded(function (encodedProfile) {
            var capabilities = {
                "browserName": "firefox",
                "firefox_profile": encodedProfile,
            };
            // TODO: iterate over capabilityMap and set key/value for each
            deferred.resolve(capabilities);
        });
        return deferred.promise;
    };
    
    getMultiCapabilities: function() {
        return q.all([
            makeFirefoxProfile({javascript.enabled: false}, {specs: ['spec1.js']})
            makeFirefoxProfile({dom.storage.enabled: false}, {specs: ['spec2.js']})
        ]);
    }
    

    If you don't want to create a helper function and want to generate the capability promises in 1 function, that's up to you. Essentially, I think the key here is to use q.all, the rest really depends on how complex your capabilities objects are and how you want to structure your code