istanbul-middleware icon indicating copy to clipboard operation
istanbul-middleware copied to clipboard

Should include example for use with Selenium

Open ORESoftware opened this issue 7 years ago • 18 comments

I think this example is very good:

https://github.com/gotwarlost/istanbul/issues/132

specifically this line is very important to know:

driver.executeScript("return window.__coverage__;")

Perhaps include something like this in the readme?


// server
var im = require('istanbul-middleware');
var express = require('express');
var app = express();
app.use('/coverage', im.createHandler());
app.listen(8888);

// test suite
// Add the following logic to every test suite which uses selenium-webdriver:
after(async function (done) {

    // http POST coverage info
    await driver.switchTo().defaultContent();
    let obj = await driver.executeScript("return window.__coverage__;")
        var str = JSON.stringify(obj);
        var options = {
            port: 8888,
            host: "localhost",
            path: "/coverage/client",
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            }
        };
        var req = http.request(options, function (res) {
            console.log("\nFinished sending coverage data.");
            done();
        });
        req.write(str);
        req.end();
})

test.after(function () {
    // at the end of the *whole* test run, download all coverage data and place it somewhere
    console.log("Closing browser.");
    driver.quit();
})

ORESoftware avatar May 31 '17 20:05 ORESoftware

Hello ORESoftware,

I am taking help of your above mentioned code to get Client Side JS coverage.I am using Istanbul for it.

But when I try to execute this line : 'driver.executeScript("return window.coverage;")', I get following error :

_ReferenceError: window is not defined
    at /Users/kapaga/Desktop/Node/webdriver.js:23:19
    at ManagedPromise.invokeCallback_ (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/promise.js:1384:14)
    at TaskQueue.execute_ (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/promise.js:3092:14)
    at TaskQueue.executeNext_ (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/promise.js:3075:27)
    at asyncRun (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/promise.js:2935:27)
    at /Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/promise.js:676:7
    at process._tickCallback (internal/process/next_tick.js:109:7)_

It seems Node doesn't support 'window' instead there is something called 'global' for Node.But when I try to use 'global', I get following error :

_WebDriverError: unknown error: global is not defined
  (Session info: chrome=59.0.3071.86)
  (Driver info: chromedriver=2.28.455517 (2c6d2707d8ea850c862f04ac066724273981e88f),platform=Mac OS X 10.12.4 x86_64)
    at WebDriverError (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/error.js:27:5)
    at Object.checkLegacyResponse (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/error.js:517:15)
    at parseHttpResponse (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/http.js:509:13)
    at doSend.then.response (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/http.js:441:30)
    at process._tickCallback (internal/process/next_tick.js:109:7)
From: Task: WebDriver.executeScript()
    at thenableWebDriverProxy.schedule (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/webdriver.js:816:17)
    at thenableWebDriverProxy.executeScript (/Users/kapaga/Desktop/Node/node_modules/selenium-webdriver/lib/webdriver.js:887:16)
    at Object.<anonymous> (/Users/kapaga/Desktop/Node/webdriver.js:20:13)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:393:7)_

Can you please help me out here? I am new to Javascriot world ( & to be precise to the Coding world, as I am a QA) so it's highely likely that I might be missing out something very silly here.

Thanks

kagarwal29 avatar Jun 14 '17 12:06 kagarwal29

I can try to help...first this article might help: https://medium.com/@the1mills/front-end-javascript-test-coverage-with-istanbul-selenium-4b2be44e3e98

You want to use window not global, because that script is executing in the browser.

Looks like I am using a different webdriver package. I am using this:

npm install --save-dev webdriver-manager

So try using that installation.

then in a bash script, I do this:

# start-selenium.sh
cd $(dirname "$0")
./node_modules/.bin/webdriver-manager update
./node_modules/.bin/webdriver-manager start

try using that, and let me know if it works. If not, we try more things.

Make sure to make the script executable, with

chmod u+x ./start-selenium.sh

then you can execute the script like so

./start-selenium.sh

ORESoftware avatar Jun 15 '17 05:06 ORESoftware

Hey @ORESoftware @gotwarlost,

After I include 'webdriver-manager' , I get following error :

var browser = new driver.Builder().usingServer().withCapabilities({'browserName': 'chrome' }).build();
              ^

TypeError: driver.Builder is not a constructor
    at Object.<anonymous> (/Users/kapaga/Desktop/Node/webdriver.js:4:15)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:393:7)
    at startup (bootstrap_node.js:150:9)
    at bootstrap_node.js:508:3

Any leads here?

Also when I try to follow https://medium.com/@the1mills/front-end-javascript-test-coverage-with-istanbul-selenium-4b2be44e3e98 , I get following error while running my test :

return async cb => {
               ^^
SyntaxError: Unexpected identifier
    at createScript (vm.js:56:10)
    at Object.runInThisContext (vm.js:97:10)
    at Module._compile (module.js:542:28)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.require (module.js:497:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/Users/kapaga/Desktop/Node/webdriver-gotwarlost.js:7:24)

Actually my Node is not able to recognize following keywords : await async after let cb req

What all libraries do i need to install for above keywords to work ? I did npm install for above mentioned libraries & used in my scripts like (var async = require('asyncawait/async'); var await = require('asyncawait/await');var let = require('let');) but node still doesn't recognize them. Please help.

kagarwal29 avatar Jun 19 '17 05:06 kagarwal29

Hey @ORESoftware @gotwarlost ,

I managed to tweak the scripts so that I can avoid keywords like await,after,let etc.Following is my code now :

My Server :

const cov = require('istanbul-middleware');
const isCollectCoverage = process.env.CDT_COLLECT_COVERAGE === 'yes';
const express = require('express');
const app = express();
const path = require('path');

if (isCollectCoverage) {
  app.use(express.static(path.join(__dirname, 'public-coverage')));
}
else {
  app.use(express.static(path.join(__dirname, 'public')));
}
// ....
if (isCollectCoverage) {
  app.use('/coverage', cov.createHandler()); // mount istanbul middleware here
}
app.listen(3000);

POST script to server :

var http = require('http');
var CircularJSON = require('circular-json');


exports.loadCoverage = function (driver) {

  //return async cb => {

    driver.switchTo().defaultContent();
    obj = driver.executeScript('return window.__coverage__;');
    str = CircularJSON.stringify(obj);
    options = {
      port: 3000,
      host: "localhost",
      path: '/coverage/client',
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      }
    };

      req = http.request(options, res => {

      console.log(' => Response status => ', res.statusCode);
      console.log(' => Finished sending coverage data.');

      data = '';
      // you must listen to data event, so that the end event will fire...
      res.on('data', d => {
        data += d;
      });

      res.once('end', function () {
        console.log('\nFinished sending coverage data => response => ', data);
      // cb();  // fire the final callback
      });
    });
    req.write(str);
    req.end();


};

My Test :

var driver = require('selenium-webdriver');
var browser = new driver.Builder().usingServer().withCapabilities({'browserName': 'chrome' }).build();

browser.get('file:///Users/kapaga/Desktop/sample.html');

const {loadCoverage} = require('./coverage_helpers');
//after.cb('send coverage data', loadCoverage(browser));
loadCoverage(browser);

I have instrumented my code as per the command mentioned in the article & injecting it in sample.html.

When I try to run my test , my page is loaded but no coverage data is reported :

 => Response status =>  404
 => Finished sending coverage data.

Finished sending coverage data => response =>  <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot POST /coverage/client</pre>
</body>
</html>

What could be the possible reason for this output ? Variable 'window__coverage__' is properly getting populated in the browser & it is getting returned to the object in the script.

kagarwal29 avatar Jun 19 '17 12:06 kagarwal29

Use NVM to upgrade to a newer version of Node.js https://github.com/creationix/nvm

You should use version 8 if possible.

Try using the original code, and then let me know if you still have problems. Make sure you are running all the Selenium code on the backend in Node.js.

e.g.: browser.get('file:///Users/kapaga/Desktop/sample.html');

the above line should be running in Node.js.

ORESoftware avatar Jun 19 '17 18:06 ORESoftware

I am running Node Version 8.1.2 (ran latest Node.js JAR file.Shall I update using NVM ? Will it make any difference?) & I am running 'browser.get('file:///Users/kapaga/Desktop/sample.html');' in Node.js only.

kagarwal29 avatar Jun 19 '17 18:06 kagarwal29

Reinstalled latest version of Node (8.1.2) using NVM & used original code but still node is not able to identify the keywords like cb,after,let etc (as mentioned in my previous comment). Can there be any ES compatibility issue here? Can you use the code which I have pasted above to run a sample test on you local so that if it runs on your local then I can try to replicate your different Library versions on my local?

kagarwal29 avatar Jun 19 '17 19:06 kagarwal29

are you sure you are using node version 8?

what happens when you run

node -v

at the command line?

make sure you do

nvm install 8
nvm use 8

and to make 8 your default version, there's a special nvm command for that

ORESoftware avatar Jun 20 '17 01:06 ORESoftware

When I do node -v i get v8.1.2. I have done 'nvm use 8.1.2' also.Still no change in the problem.

kagarwal29 avatar Jun 20 '17 05:06 kagarwal29

Is it your IDE that cannot identify the keywords or the actually node.js runtime? what is the error that you get?

ORESoftware avatar Jun 20 '17 05:06 ORESoftware

I get following error :

after.cb('send coverage data', loadCoverage(browser));
^

ReferenceError: after is not defined
    at Object.<anonymous> (/Users/kapaga/Desktop/Node/webdriver-gotwarlost.js:8:1)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)
    at Function.Module.runMain (module.js:605:10)
    at startup (bootstrap_node.js:158:16)
    at bootstrap_node.js:575:3

It's Node.js runtime which cannot identify the keywords.

kagarwal29 avatar Jun 20 '17 05:06 kagarwal29

You are getting closer.

Ok, so after is a global variable that does not have to with Node or JavaScript, but it is from a test harness library like Jasmine or Mocha. In my case, after.cb is from the Suman library, which is similar to Mocha, etc.

There are a lot of moving pieces here.

First install Mocha:

npm install -g mocha

Create this simple file:

// bar.js
describe('foo', function(){

   before('get home',function(){
       return browser.get(xxx);  // fix this with the right url, this returns a promise
   });

   it('simple test', function(){
       // make some call to selenium to do some actions in your front-end code
   });

   after('load coverage', function(){
      return driver.switchTo().defaultContent().then(function(){
          return driver.executeScript('return window.__coverage__;');
      }).then(function(obj){
          // must return a promise
          return postTheDataToYourServerRunningIstanbulMiddleWare(obj);  
      });
   });
});

then run:

mocha bar.js

mocha will load the global variables into the environment. You probably will have a few more problems but we can work through them.

The before/after/it hooks in Mocha (and all similar libraries) will handle promises returned from the callback.

At the end I would like to ask you to choose to use Suman instead of Mocha :)

Note that the after hook above could be re-written as:

  after('load coverage', async function(){
       await driver.switchTo().defaultContent().then(function(){
       let obj = await driver.executeScript('return window.__coverage__;');
       return postTheDataToYourServerRunningIstanbulMiddleWare(obj);  
  });

async/await is a native wrapper around promises + generators.

note that for this to work, either way,

postTheDataToYourServerRunningIstanbulMiddleWare(obj);

must return a promise.

ORESoftware avatar Jun 20 '17 06:06 ORESoftware

Sure.Let me try it.

kagarwal29 avatar Jun 20 '17 06:06 kagarwal29

Finally I am able to see coverage report using following code :

var http = require('http');
var CircularJSON = require('circular-json');

var driver = require('selenium-webdriver');
var browser = new driver.Builder().usingServer().withCapabilities({'browserName': 'chrome' }).build();

describe('selenium test', function(){

this.timeout(15000);

   before('Launching Browser',function(){

       return browser.get('file:///Users/kapaga/Desktop/sample.html');


it("Posting Data to Istanbul Middleware",function (done) {

    browser.switchTo().defaultContent();
    browser.executeScript("return window.__coverage__;").then(function (obj) {
        var str = CircularJSON.stringify(obj);
        var options = {
            port: 3000,
            host: "localhost",
            path: "/coverage/client",
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            }
        };
        var req = http.request(options, function (res) {
            console.log("\nFinished sending coverage data.");
            done();
        });
        req.write(str);
        req.end();
    });
});

after(function () {
    // at the end of the *whole* test run, download all coverage data and place it somewhere
    console.log("Closing browser.");
    browser.quit();
});

});

Thanks a ton for your help & guidance. :) :) 👍

But when I try to use code mentioned here : https://medium.com/@the1mills/front-end-javascript-test-coverage-with-istanbul-selenium-4b2be44e3e98 , I get following error :

after.cb('send coverage data', loadCoverage(browser));
      ^

TypeError: after.cb is not a function
    at Object.<anonymous> (/Users/kapaga/Desktop/Node/test/webdriver-gotwarlost.js:10:7)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)
    at /Users/kapaga/.nvm/versions/node/v8.1.2/lib/node_modules/mocha/lib/mocha.js:230:27
    at Array.forEach (native)
    at Mocha.loadFiles (/Users/kapaga/.nvm/versions/node/v8.1.2/lib/node_modules/mocha/lib/mocha.js:227:14)
    at Mocha.run (/Users/kapaga/.nvm/versions/node/v8.1.2/lib/node_modules/mocha/lib/mocha.js:495:10)
    at Object.<anonymous> (/Users/kapaga/.nvm/versions/node/v8.1.2/lib/node_modules/mocha/bin/_mocha:469:18)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)
    at Function.Module.runMain (module.js:605:10)
    at startup (bootstrap_node.js:158:16)
    at bootstrap_node.js:575:3

Any lead here?

kagarwal29 avatar Jun 20 '17 14:06 kagarwal29

after.cb is from the Suman library. Mocha does not have that style. Just use Mocha, if it is working for you.

ORESoftware avatar Jun 20 '17 19:06 ORESoftware

Sure. Thanks a lot. :)

kagarwal29 avatar Jun 21 '17 06:06 kagarwal29

Np at all...if you think I know a thing or two, definitely star this repo

https://github.com/sumanjs/suman

when I release that project it's gonna be so huge

ORESoftware avatar Jun 21 '17 07:06 ORESoftware

by the way, in your code above, you want to do:

return browser.quit();

this returns the promise to the caller, so the caller can wait for the promise to resolve before shutting down the process.

ORESoftware avatar Jun 21 '17 07:06 ORESoftware