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

browser.takeScreenshot not working when called from 'specDone' (jasmine2 reporter) #1753

Closed
PaddyMann opened this issue Jan 26, 2015 · 11 comments

Comments

@PaddyMann
Copy link

I am setting up my tests to take screenshots when tests fail, as well as at other times that I specify.

The code for taking a screenshot is very standard, with some wrapping around it to specify where the screenshots go.

...
browser.takeScreenshot().then(function (png) {
  var stream = fs.createWriteStream(directory + '/' + name + '.png');
  stream.write(new Buffer(png, 'base64'));
  stream.end();
});
...

When I call this from within a spec, it works perfectly.

However, I have tried to call it from within 'specDone' and while it reaches browser.takeScreenshot(), the promise never calls then() and the screenshot is not produced. No error is displayed in console either.

Here is my very simple reporter:

var myReporter = {
  specDone: function (result) {
    if (result.status !== 'passed') {
      screenshot.takeScreenshotOnFail(result.fullName.replace(/ /g, '_'));
    }
  }
};

jasmine.getEnv().addReporter(myReporter);

Any insights into what's happening and how to overcome it would be appreciated :)

@hankduan
Copy link
Contributor

This is likely related to the fact that takeScreenshot is asynchronous, and that specDone does not know to wait for it.

@juliemr
Copy link
Member

juliemr commented Jan 26, 2015

I just tried this, and it works for me (reaches takeScreenshot.then, outputs the screenshot). Maybe there's an issue with how you are registering the reporter?

My simple config looks like:

exports.config = {
  directConnect:true,
  capabilities: ...,
  specs: ...,
  framework: 'jasmine2',
  onPrepare: function() {
    jasmine.getEnv().addReporter({
      specDone: function(result) {
        browser.takeScreenshot().then(function(png) {
          console.dir(png); // This will get output to the console as expected.
        });
      }
    });
  }
}

edit: fix function

@lightsaway
Copy link

Is there a way how to setup screenshoting of failing expectation using jasmine2? I don't think that taking screenshots in the end of the spec is not good for me.

@mlison
Copy link

mlison commented Mar 14, 2015

@juliemr I'm seeing the same problem. The thing is that the callback will indeed be called, but not before suiteDone / jasmineDone. Basically in my case, the order in which they finish is roughly this: specDone (triggers screenshot) -> suiteDone -> jasmineDone -> screenshot callback triggers.

This happens to be a problem only if you require some information from the screenshot callback in the following step (specDone / jasmineDone).

@juliemr juliemr modified the milestone: Upcoming Mar 17, 2015
@juliemr
Copy link
Member

juliemr commented Mar 17, 2015

I haven't heard from the initial reporter on the issue, but I think it boils down to this: #1938

Closing to continue discussion there.

@juliemr juliemr closed this as completed Mar 17, 2015
@eddywashere
Copy link

If anyone else comes to this issue, I found that adding an afterAll to my specs with the following snippet, gives the browser.takeScreenshot just enough time to execute.

afterAll(function(done){
    process.nextTick(done);
});

@PaddyMann
Copy link
Author

Apologies for going quiet - not sure why I missed the helpful responses to my initial query.

My issue does relate to it not waiting for async work to complete before quitting. If I just take the screenshot, then it does work. Before taking the screenshot, I was using browser.getCapabilities and then also making the directory using mkdirp. Turns out the mkdirp step wasn't needed, and I now call getCapabilities during startup, so screenshots on fail now working as hoped :)

@PaddyMann
Copy link
Author

OK - I lied. It appeared to work locally, but the above fails when running on codeship, and mkdirp is definitely needed on local as well.

Trying the process.nextTick hack :( (:( because it feels hacky and because I'll need to add it to every spec file?)

@PaddyMann
Copy link
Author

Have refactored code to take the screenshot immediately rather than waiting for mkdirp and browser.getCapabilities to run. Now works fine, no process.nextTick hack needed.

Here's my screenshot.js component:

/*
 * Expose methods for taking screenshots
 */
'use strict';

var fs = require('fs');
var Promise = require('promise');
var mkdirp = require('mkdirp');

module.exports = {
  takeScreenshot: takeScreenshot,
  takeScreenshotOnFail: takeScreenshotOnFail
};

function takeScreenshot (name)
{
  capture(name, 'tmp/screenshots/compare');
}

function takeScreenshotOnFail (name)
{
  capture(name, 'tmp/screenshots/fails');
}

function capture (name, baseDir)
{
  baseDir = baseDir || 'tmp/screenshots';

  Promise.all([
    browser.takeScreenshot(),
    browser.getCapabilities()
  ]).then(function (values) {
    var png = values[0];
    var caps = values[1].caps_;
    var screenshotDir = __dirname + '/../' + baseDir + '/' + caps.platform + '_' + caps.browserName + '_' + caps.version;

    mkdirp(screenshotDir, function(err) {
      var stream = fs.createWriteStream(screenshotDir + '/' + name + '.png');
      stream.write(new Buffer(png, 'base64'));
      stream.end();
      return true;
    });
  });
}

This is triggered by specDone as in my initial post above

@fruwe
Copy link

fruwe commented Mar 18, 2016

@PaddyMann Thanks for sharing!! Worked like a charm

@yjaaidi
Copy link

yjaaidi commented Oct 12, 2017

Hello,

As you can see in our fork of protractor-beautiful-reporter https://github.com/wishtack/protractor-beautiful-reporter/blob/master/index.js we are using the following pattern:

class Reporter {

    _asyncFlow: Promise<any>;
    
    jasmineStarted() {

        /* Wait for async tasks triggered by `specDone`. */
        beforeEach(async () => {
            
            await this._asyncFlow;
            this._asyncFlow = null;
            
        });
        
    }
    
    specDone(result) {
        
        this._asyncFlow = this._asyncSpecDone(result);
        
    }
    
    async _asyncSpecDone(result) {
        
        // @todo: Do your async stuff here depending on `result.status`, take screenshots etc...
        // await takeScreenshot();
        
    }

}

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

No branches or pull requests

8 participants