Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Feature Suggestion: Support multiple browser instances from single test #381

Closed
bobthekingofegypt opened this issue Dec 30, 2013 · 35 comments

Comments

@bobthekingofegypt
Copy link

Currently I am working on some socket based multi-player games using angular. In order to perform an e2e test I need to have two browsers talk to each other as the games wont start till 2 people join.

I was wondering if this could be something that is added on top of protractor.
I kinda hacked up a proof of concept to allow me to test users joining and chatting on the game.
bobthekingofegypt@8ac849a

As a concept it appears to work ok, it allows you to write tests like

    var browserOne = protractors[0].browser;
    var browserTwo = protractors[1].browser;

    var elementTwo = protractors[1].element;

    var byOne = protractors[0].by;
    var byTwo = protractors[1].by;

    browserOne.get('http://127.0.0.1:4999/');
    browserTwo.get('http://127.0.0.1:4999/');

    var joinButton = browserOne.element(byOne.css('[value="Join"]'));
    joinButton.click();

    element(by.model('input.name')).sendKeys('Bob');
    browserOne.element(byOne.css('[value="OK"]')).click();

    var playerList = element.all(by.repeater('player in players'));
    expect(playerList.count()).toBe(1);

    var playerListOther = elementTwo.all(byTwo.repeater('player in players'));
    expect(playerListOther.count()).toBe(1);

This change breaks certain things like saucelabs support and it's not very complete but it is meant as a conversation starter.

It is quite a niche use case but is this something you would consider supporting in protractor?

@juliemr
Copy link
Member

juliemr commented Dec 31, 2013

I'm so happy you brought this up, because a while ago we'd had a discussion and decided that no one would ever actually use that functionality!

So, you could do this now by basically rewriting runner.js (which is what you've started). I don't think there's any way to generalize your use case enough that it would be worth it to make it reusable, so I'd encourage you to just go ahead and write your own runner.js.

If anyone else would use this though, I'd love for them to chime in here!

@tennisgent
Copy link

We would definitely use this. We are writing a web app that allows insurance policy holders to communicate with construction contractors who are supposed to do repairs on the policy holders' homes.

Currently, we have to write e2e tests for each side of the app separately. We have to write one test that signs in as a policy holder, does some setup and then sends off a communication to the contractor. And then we have to log out as a policy holder, log in as a contractor, and then make sure that the communication arrived in the contractor's inbox as expected.

However, if the functionality described above were possible, then we could run two parallel versions of the browser at the same time where one is signed in as a policy holder and the other is signed in as a contractor and we could instantly verify that the communications can go back and forth as expected.

That would be a huge benefit to us. I can imagine there are many other such websites out there that could use this type of functionality.

@christothes
Copy link

I'd like to use this as well. I'd like to test a game that needs multiple instances to exercise all scenarios

@hanssgo
Copy link

hanssgo commented Jan 13, 2014

Would definitely use this as well! Been logging out and logging in as different users as well, with this support we would no longer have to do that =)

@christothes
Copy link

@juliemr Curious why this couldn't be generalized?

I was thinking it might make more sense if the concept of a "primary driver" remained, with a config option to specify n number of "helper drivers" that could be kept in an array similar to the example from @bobthekingofegypt. Would this approach make it any less intrusive to the mainline use case while allowing the multi-driver flexibility?

Granted I don't understand how saucelab compatibility comes into play here. However, if two two concepts simply aren't compatible, that could be accounted for in the config logic.

I wouldn't mind attempting a PR.

@elgalu
Copy link
Contributor

elgalu commented Mar 5, 2014

I need this to test instant messaging and notifications between two logged in users.

My suggestion is similar to @bobthekingofegypt but using blocks instead of elementOne, elementTwo

And having browser1, browser2, ... kind of globals:

By default browser would point to browser1 and all expectations would run by default on browser1 unless browser2 is specified with some DSL like browser2.run(..{...}...); making the feature backward compatible.

Sample usage of browser2

it('works with 2 logged in users', function() {
  browser1.get('page1');
  browser2.get('page2');
  expect($('.div').getText()).toContain('hi'); // always defaults to browser1
  browser2.run(function() {
    $('button#send').click();
    expect($('.div').getText()).toContain('hello you');
  });
});

@adamduffy
Copy link

+1 need for this.

@hanssgo
Copy link

hanssgo commented Mar 12, 2014

+1. Would be able to use this as well. Saves having to log out and log in
as different users.
On Mar 12, 2014 10:22 AM, "adamduffy" [email protected] wrote:

+1 need for thtis.

Reply to this email directly or view it on GitHubhttps://github.com//issues/381#issuecomment-37437150
.

@caitp
Copy link

caitp commented Mar 12, 2014

/sub

@elgalu
Copy link
Contributor

elgalu commented Mar 13, 2014

Will leave a reminder here that this feature won't work on IE:
https://code.google.com/p/selenium/wiki/FrequentlyAskedQuestions#Q:_Can_I_run_multiple_instances_of_the_WebDriver_sub-classes?

Not sure about Safari, probably won't support this either.

@bobthekingofegypt
Copy link
Author

It was a while ago that I did this, and I haven't been working with Angular/protractor since so it is a bit hazy.

But looking at my diff I don't think I did anything beyond what was required to make a proof of concept work. All the promise work is because you need to block till all sessions are ready, and then make sure all sessions terminate at the end of the test. I also left in some code so single browser tests would work as normal. The fork does work, I used it for two projects; it does have flaws though.

I haven't tracked the changes in protractor but if what you want to do is simple you can try checking out my fork and npm link it, I changed the name from protractor to protractor-multi so I could run them both at the same time.

@toedter
Copy link

toedter commented May 9, 2014

+1 for this. I would like to test asynchronous pub/sub with 2 browsers.

@Mikrovolnovka
Copy link

You can generalize this with implementing the ability of parallelization tests with scaling that is not limited to 2.

Sometimes we implement tests that are not affecting each other and can be run in parallel.
We would like to have ability of specifying different sets of specs.
I guess this should be possible both with Selenium Standalone server and direct usage of drivers.
I tried to run 2 different instances of protractor on single local machine (but in different bash windows, hehe) at a time using one instance of Selenium Standalone server and they worked in parallel well.

So, the POC is to implement running parallel tests within single protractor instance (with single output and ability to collect results to single report). Maybe there can be way to use something like Async.JS to run different specs in threads.

Maybe there is a way to do that with some Grunt package, but it is not convenient to have many Protractor config files, we would like to have all configs in one file.

Thanks in advance for your reply.

@orcaman
Copy link

orcaman commented May 17, 2014

+1 for this (writing a websockets app that could benefit from this)

@rafaelbattesti
Copy link

+1 still needing some support...
@juliemr Could you give me some directions?

From issue #569...

I need 2 instances of browser running within a single test and must be able to manipulate them individually. I haven't found any info on how to perform it. Would be glad if you could enlighten me. This is a definite roadblock for my test suite. Thanks.

Thanks @hankduan. The problem I see is. If I use multiCapabilities, I can't manipulate both instances as I want to. The action of a user in one browser must reflect in the other browser. Like in google docs, when a user writes in a shared document, the other can see what is happening. This is basically what I must test. And if the second user deletes what had been written beforehand, the first must see it.

@Mikrovolnovka
Copy link

@rafaelbattesti wrote: "Thanks @hankduan. The problem I see is. If I use multiCapabilities, I can't manipulate both instances as I want to. The action of a user in one browser must reflect in the other browser. Like in google docs, when a user writes in a shared document, the other can see what is happening. This is basically what I must test. And if the second user deletes what had been written beforehand, the first must see it."

What about using "wait" functions. For about, you can implement something like "waitForElement" and in one spec file (1) start to wait with your timeout (e.g. 30s), in other spec file (2) run test that should reflect the first browser. spec file (2) makes actions and after them new element appears. spec file (1) sees that his element appears and continues his actions.
Does this make sense?

@hankduan
Copy link
Contributor

@rafaelbattesti If your backend is not mocked out, you can give @Mikrovolnovka's method a try. It might be a little flakey some times, but that is the best solution for the use case at the moment as we do not have any "official" way of doing this right now.

@juliemr juliemr added this to the Icebox milestone Jun 18, 2014
@nnaffar
Copy link

nnaffar commented Jun 22, 2014

+1 . I would like to test client 2 client web app (#949).
does anyone knows how can i do it?
thanks

@carlhopf
Copy link

+1 as this would be very very useful

@Elijen
Copy link

Elijen commented Aug 8, 2014

+1

1 similar comment
@ntrrgc
Copy link

ntrrgc commented Aug 10, 2014

+1

@selahlynch
Copy link

Hi, we would use this too!
http://simulations.wharton.upenn.edu/

@noamokman
Copy link
Contributor

+1

@lam2558
Copy link

lam2558 commented Sep 19, 2014

This might be a work around. Please let me know if this a a good way of doing it or not.

conf.js
  multiCapabilities: [
  {  browserName: 'chrome', },
  {  browserName: 'firefox',  }],

test.js
it('open login page', function () {
    var browserType = null;
    console.log("browser base URL =<<  "+ browser.baseUrl);
    browser.getCapabilities().then(function (cap) {
      console.log(cap.caps_.browserName);
      browserType = cap.caps_.browserName;
    });
  });

it('test something with two browser open up simultaneously', function (){
if(browserType == 'firefox') { login as user1 and send message "hello" }
if(browserType == 'chrome') {login as user2 and wait for message "hello" and response "hi"}
});

I'm essentially opening two different browser and in the same test script, defining what each browser should act as different user.

@moskrc
Copy link

moskrc commented Oct 10, 2014

+1

@ceelian
Copy link

ceelian commented Oct 13, 2014

I am not sure if I solved the exact same problem already a year ago.

We also had an interacting socket.io app. We wrote the protractor e2e tests one year ago, so the code is right now only compatible with a very early version of protractor (0.12 if I remember it right).

The following code is just a small demo code for a blog post I wrote a year ago about our first steps with protractor in an interacting dual browser setup (Source in german http://blog.cnc.io/allgemein/e2e-tests-in-angularjs-with-protractor/)

Maybe we can translate this code to work with version 1.3.1 and have this feature out of the box?

/*###############################################
 Start the selenium server:
 java -jar selenium/selenium-server-standalone-2.35.0.jar \
 -Dwebdriver.chrome.driver=node_modules/chromedriver/bin/chromedriver

 Start the tests:
 jasmine-node --verbose path/to/my/e2e/tests/example-spec.js

//###############################################*/
//It is important to use the protractor selenium-webdriver
var webdriver = require('protractor/node_modules/selenium-webdriver');
var protractor = require('protractor');
require('protractor/jasminewd');

describe('yeoman angularjs generator app', function() {

    //create a driver which starts a chrome browser
    var driver1 = new webdriver.Builder()
            .usingServer('http://localhost:4444/wd/hub')
            .withCapabilities(webdriver.Capabilities.chrome()).build();

    //create another driver which starts a firefox browser
    var driver2 = new webdriver.Builder()
            .usingServer('http://localhost:4444/wd/hub')
            .withCapabilities(webdriver.Capabilities.firefox()).build();

    //set timeout and wrap the webdriver instance in a protractor instance
    driver1.manage().timeouts().setScriptTimeout(15000);
    var ptor1 = protractor.wrapDriver(driver1);

    driver2.manage().timeouts().setScriptTimeout(15000);
    var ptor2 = protractor.wrapDriver(driver2);

    //the test scenario
    it('should have a list of awesome things', function() {
        //load web application in both browser
        ptor1.get('http://localhost:9000/?myspecialparam=forbrowserone');
        ptor2.get('http://localhost:9000/?myspecialparam=forbrowsertwo');

        //run test in first browser (chrome)
        var thingslistone = ptor.findElements(
                    protractor.By.repeater('thing in awesomeThings'));
        thingslistone.then(function(arr) {
            expect(arr.length).toEqual(3);
            expect(arr[0].getText()).toEqual('HTML5 Boilerplate');
            expect(arr[1].getText()).toEqual('AngularJS');
            expect(arr[2].getText()).toEqual('Karma');
        });

        //run test in second browser (firefox)
        var thingslisttwo = ptor2.findElements(
                protractor.By.repeater('thing in awesomeThings'));
        thingslisttwo.then(function(arr) {
            expect(arr.length).toEqual(3);
            expect(arr[0].getText()).toEqual('HTML5 Boilerplate');
            expect(arr[1].getText()).toEqual('AngularJS');
            expect(arr[2].getText()).toEqual('Karma');
        });

    }, 20000);

    //needed to quit the browser after all tests are executed
    it('afterAll', function() {
        driver1.quit();
        driver2.quit();
    })
});

@lam2558
Copy link

lam2558 commented Oct 16, 2014

This is my thought:

Since I am using multiCapabilities to start multiple threads, the global
browserInstanceCount variable in protractor.conf may not be shared by all
threads.

I am trying a new way of identifying browsers so that it no longer rely on
the browser type. The new approach is to assign ticket number to new
browser. The ticket number is saved in a text file and will accumulate
every time it is read by a new browser. So now browser is identified by
ticket number instead of browser type. I tried with 4 same or mixed browser
types and it works so far. The file, however, has to be removed by Jenkins
or manually each time before script starts.

The ticket management is written in the conf.js :
var location = "ticket";

if(fs.existsSync(location)){ //when file exists, add 1 for the new
thread
var number = null;
number - fs.readFileSync(location).toString();
number = parseInt(number) + 1;
fs.writeFileSync(location, number);
browser.params.ticketNumber = number;
}
else{ // when file does not exists, assign 1 for the first thread
browser.params.ticketNumber = "1";
fs.writeFileSync(location, browser.params.ticketNumber);
}

In your spec:
if(browser.params.userTicket == "1") {
// do steps for browser #1
} else if(browser.params.userTicket == "2"){
// do steps for browser #2
}....

If the global variable in protractor.conf works then your solution is
better since no file management required. Please try it out and let us know.

On Thu, Oct 16, 2014 at 8:51 AM, carlhopf [email protected] wrote:

Quickly hacked something together on top of angular 1.3.1, to start and
get a second global browser object. Works fine so far! Be warned: the code
is extremely ugly and only works for 'chromeOnly: true' configs.

Just wanted to share the code, in case anyone needs a starting point in
getting a second browser instance running.

What about a protractor.conf option like 'browserInstanceCount', then
simply launch this many instances of drivers for each running test suite
and make them available as a global browsers[] array?

carlhopf@e92e62e
https://github.com/carlhopf/protractor/commit/e92e62eff80ebfb4f8e2ebb1e39b4699170d55fc

To test:

git clone https://github.com/carlhopf/protractor.git
cd protractor
npm install
npm link
webdriver-manager update


Reply to this email directly or view it on GitHub
#381 (comment).

@carlhopf
Copy link

Sorry, I've had to delete my old comment due to a bug. Here is my new take on the problem to allow multiple browsers windows/instances for protractor 1.3.1: https://github.com/carlhopf/protractor/commit/dd76f72bcc2e6fc338b95e3465a19034b4591241

This allows to call

it('should create, test, and close a new browser window', function() {
    var browser2 = newBrowser();
    browser2.get('http://localhost:8000');
    expect(browser2.element(by.css('my-missing-element')).isPresent()).toBe(false);
    quitBrowser(browser2);
});

and you'll get a new browser window to work with (supports as many as you'd like to use). All new windows will also automatically close when protractor quits.

NOTE: you must add "chromeOnly: true;" to protractor.conf.js, I've just made lib/driverProviders/chome.js compatible so far!

Test it out yourself:

git clone https://github.com/carlhopf/protractor
cd protractor
npm install
npm link
webdriver-manager update

@carlhopf
Copy link

Here is another workaround i'd like to propose for multiple windows/instances for protractor 1.3.1: https://github.com/carlhopf/protractor/commit/9aa1a7e4b10ca3becd699ea7f3ae63158dfd8b9e

This gives you newBrowser(), quitBrowser(index) and switchBrowser(index) and assigns the currently selected browser to the global protractor objects (browser, element, protractor.getInstance(), ...). So there is no need to change test syntax, simply switchBrowser() and continue as before.


describe('should open 2 browsers', function() {
    beforeEach(function() {
        newBrowser();
    });

    it('should open two windows', function() {
        browser.get('http://localhost:8001');

        switchBrowser(1);
        browser.get('http://127.0.0.1:8001');

        browser.executeAsyncScript(function(callback) {
            callback(window.location.href.indexOf('127.0.0.1') !== -1);
        }).then(function(res) {
            expect(res).toBe(true);
        });

        switchBrowser(0);

        browser.executeAsyncScript(function(callback) {
            callback(window.location.href.indexOf('127.0.0.1') === -1);
        }).then(function(res) {
            expect(res).toBe(true);
        });
    });

    afterEach(function() {
        quitBrowser(1);
        expect(browsers.length).toBe(1);
    });
});

NOTE: you must add "chromeOnly: true;" to protractor.conf.js, I've just made lib/driverProviders/chome.js compatible so far!

Test it out yourself:

npm install -g git://github.com/carlhopf/protractor.git#switchbrowser
webdriver-manager update

@Bessonov
Copy link

"I'm so happy you brought this up, because a while ago we'd had a discussion and decided that no one would ever actually use that functionality!"

I think at least every "real-time" app need this testing functionality. At least for chrome there is no problem to open new window through:
var win = prt.driver.executeScript('window.open("http://localhost/", "windowName", "width=1024,height=768");');
(see http://stackoverflow.com/a/726803/926620 ). But the problem then is shared session. To overcome this limitation I use different hostname (for two windows 127.0.0.1 and localhost), like described in http://stackoverflow.com/a/23551878/926620 .

This feature is very important for me too.

@ceelian
Copy link

ceelian commented Nov 24, 2014

Got my multi-browser test to run again with the "vanilla" protractor package. Perfect for chat or any other multi-window live interaction web project testing.

Just had to change the following includes:

var webdriver = require('selenium-webdriver');
var protractor = require('protractor');
require('jasminewd');

The full example code is:

/*###############################################
 Start the selenium server:
 java -jar selenium/selenium-server-standalone-2.35.0.jar \
 -Dwebdriver.chrome.driver=node_modules/chromedriver/bin/chromedriver

 Start the tests:
 jasmine-node --verbose path/to/my/e2e/tests/example-spec.js

//###############################################*/
var webdriver = require('selenium-webdriver');
var protractor = require('protractor');
require('jasminewd');

describe('yeoman angularjs generator app', function() {

    //create a driver which starts a chrome browser
    var driver1 = new webdriver.Builder()
            .usingServer('http://localhost:4444/wd/hub')
            .withCapabilities(webdriver.Capabilities.chrome()).build();

    //create another driver which starts a firefox browser
    var driver2 = new webdriver.Builder()
            .usingServer('http://localhost:4444/wd/hub')
            .withCapabilities(webdriver.Capabilities.firefox()).build();

    //set timeout and wrap the webdriver instance in a protractor instance
    driver1.manage().timeouts().setScriptTimeout(15000);
    var ptor1 = protractor.wrapDriver(driver1);

    driver2.manage().timeouts().setScriptTimeout(15000);
    var ptor2 = protractor.wrapDriver(driver2);

    //the test scenario
    it('should have a list of awesome things', function() {
        //load web application in both browser
        ptor1.get('http://localhost:9000/?myspecialparam=forbrowserone');
        ptor2.get('http://localhost:9000/?myspecialparam=forbrowsertwo');

        //run test in first browser (chrome)
        var thingslistone = ptor.findElements(
                    protractor.By.repeater('thing in awesomeThings'));
        thingslistone.then(function(arr) {
            expect(arr.length).toEqual(3);
            expect(arr[0].getText()).toEqual('HTML5 Boilerplate');
            expect(arr[1].getText()).toEqual('AngularJS');
            expect(arr[2].getText()).toEqual('Karma');
        });

        //run test in second browser (firefox)
        var thingslisttwo = ptor2.findElements(
                protractor.By.repeater('thing in awesomeThings'));
        thingslisttwo.then(function(arr) {
            expect(arr.length).toEqual(3);
            expect(arr[0].getText()).toEqual('HTML5 Boilerplate');
            expect(arr[1].getText()).toEqual('AngularJS');
            expect(arr[2].getText()).toEqual('Karma');
        });

    }, 20000);

    //needed to quit the browser after all tests are executed
    it('afterAll', function() {
        driver1.quit();
        driver2.quit();
    })
});

I have not tested it a lot, i just had the time to get it running again. Any suggestions are welcome if there are some bugs or misconceptions in the code.

@hankduan
Copy link
Contributor

Implemented with 0bbfd2b. This will be released in protractor 1.5.0

@svapreddy
Copy link

@hankduan Can I get a documentation link for how to control multiple browsers for instant messaging kind of applications. I have tried searching for this and I am unable to find some code except multiCapabilities.

@hankduan
Copy link
Contributor

note to self: I should work on the docs.

For now, read the api annotation: https://github.com/angular/protractor/blob/master/lib/runner.js#L190
There are also a lot of examples here: https://github.com/angular/protractor/blob/master/spec/interaction/interaction_spec.js

@nishankkumar1994
Copy link

Please make sure to install
npm install protractor

Update web driver manager
webdriver-manager update

Run this command from your root
node node_modules\protractor\bin\webdriver-manager update

Now start up a server with:
webdriver-manager start

Also make sure that your protractor.conf.js file has below line
// baseUrl: 'http://localhost:4200/',
seleniumAddress: 'http://localhost:4444/wd/hub/',

Now run your e2e tests on different browesers
ng e2e

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.