How do I take a screenshot when a test in internjs fails? - screenshot

I am having issues figuring out how to take a screenshot ONLY when a test fails in InternJs. I have this simple test in my registerSuite;
'verify google homepage': function () {
var url = 'https://www.google.com/';
return this.remote
.get(url)
.getCurrentUrl()
.then(function (data) {
assert.strictEqual(data, url, 'Incorrect URL');
})
.findByName('q')
.click()
}
I can simply create a screenshot using the following code;
.takeScreenshot
.then(function (data) {
fs.writeFileSync('/path/to/some/file', data, 'base64');
)}
I want to only take a screenshot, if the above test fails the assertion or is unable to find the locator.
I looked into the afterEach method, but I can't figure out how to get the status of the last test to apply a conditional.
So my question is, has anyone setup their internjs test to only take screenshots on failures and how was it accomplished?

It is not currently possible to interact with the currently executing test from beforeEach or afterEach methods; this capability is coming in the next version of Intern.
Selenium server, by default, provides a screenshot on every Selenium command failure, which is a Buffer object on the error.detail.screen property. If a Selenium command fails, just use this property which already has the screenshot waiting for you.
For assertion failures, you can create a simple promise helper to take a screenshot for you:
function screenshotOnError(callback) {
return function () {
try {
return callback.apply(this, arguments);
}
catch (error) {
return this.remote.takeScreenshot().then(function (buffer) {
fs.writeFileSync('/path/to/some/file', buffer);
throw error;
});
}
};
}
// ...
'verify google homepage': function () {
return this.remote.get(url).getCurrentUrl().then(screenshotOnError(function (actualUrl) {
assert.strictEqual(actualUrl, url);
}));
}
If it’s too inconvenient to wrap all your callbacks manually like this, you can also create and use a custom interface for registering your tests that wraps the test functions automatically for you in a similar manner. I’ll leave that as an exercise for the reader.

You can use catch method at the end of your chain and use error.detail.screen as suggested by C Snover.
'verify google homepage': function () {
return this.remote
.get(require.toUrl('./fixture.html'))
.findById('operation')
.click()
.type('hello, world')
.end()
.findById('submit')
.click()
.end()
.catch(function(error){
fs.writeFileSync('/tmp/screenshot.png', error.detail.screen);
})
}

I've been playing with this today and have managed to get it for an entire suite rather than needing to add the code to every single test which seems quite needless.
var counter = -1,
suite = {
beforeEach: function () {
counter++;
},
afterEach: function () {
var currentTest = this.tests[counter];
if (!currentTest.error) {
return;
}
this.remote
.takeScreenshot().then(function (buffer) {
if (!fs.existsSync(path)) {
fs.mkdirSync(path);
}
fs.writeFileSync('/tmp/' + currentTest.name + '.png', buffer);
});
}
};
The annoying thing you will need to do is do this for every test suite rather than "globally" but is much better than doing it for every test.

Building on the answer by Hugo Oshiro,
// tests/support/CleanScreenshots.js
define([
'intern/dojo/node!path',
'intern/dojo/node!del',
], function(path, del) {
return new Promise((resolve, reject) => {
let directory = 'tests/screenshots';
del(path.join(directory, '**/*'))
.then(resolve)
.catch(reject);
});
});
Then in your intern config:
/* global define */
define([
'tests/support/CleanScreenshots'
], function (CleanScreenshots) {
return {
...
setup: function () {
return CleanScreenshots();
},
...
};
});

According to this issue, starting with the Intern 3.0 you can do a custom reporter that take an Screenshots when test fail. So you can centralize it in a simple way, just referencing the custom reporter in your config.js. In my case, what can I just add a reporter array in the config.js with the path to my custom array:
reporters: [
{ id: 'tests/support/ScreenShot' }
],
than I made an custom reporter overriding testFail:
'use strict';
define([
'intern/dojo/node!fs',
], function(fs) {
function ScreenShot(config) {
config = config || {};
}
ScreenShot.prototype.testFail = function(test) {
test.remote.takeScreenshot().then(function(buffer) {
try {
fs.writeFileSync('./screenshots/' + test.parent.name.replace(/ /g, '') + '-' +
test.name.replace(/ /g, '') + '.png', buffer);
} catch (err) {
console.log('Failed to take a screenshot: ' + err);
}
});
};
return ScreenShot;
});
Pay attention to the relative paths both to reference the custom reporter and the place for screenshots. They all seems to be taken considering where you run intern-runner, not the place the source files are located.
For more info about custom reporters go to this page.

Related

Jest testing :mockResolveValue with delay

How to mockReturnValue with delay in jest testing?
The intention of this test is to mock method as unsettled on flushpromise. Let's say,
If we have promise which is mocked on test class. Once flushpromise , It would resolve the promise and we can able to assert the statement either as resolved or rejected.
Requirement for testclass is that the promise shouldn't be resolved or rejected after flushpromise (test Unsettled).
There are two possible way , we can achieve this by
making mockReturnValue return empty promise.
using jeskfaketimer to delay the mockReturnvalue
Both the above option is expected to give unsettled promise on assertion.
Although, The second option doesn't seem working with following code. Any input is appreciated.
Tried the following code.
`
import callout from ".../callout.."
jest.mock(
"../callout..",
()=>{
return{
default:jest.fn(),
};
},
{virtual: true}
);
const { setImmediate } = require("timers");
function flushPromises()
{
return new Promise((resolve)=> setImmediate(resolve));
}
`it("test delay", async()=>{
jest.useFakeTimers();
callout.mockResolvedValue(()=> Promise(resolve=>setTimeout(()=>resolve(),2000)));
jest.advanceTimersByTime(20);
await flushpromises();
expect(callout).not.toHavebeencalled();
jest.advanceTimersByTime(2000);
expect(callout).toHavebeencalled();
});
`
I am able to resolve the issue with the following change. Basically, I replaced the setTimeout with a wait custom function as shown below.
import callout from ".../callout.."
jest.mock( "../callout..",()=>{
return{
default:jest.fn(),
};
},
{virtual: true}
);
const { setImmediate } = require("timers");
function flushPromises()
{
return new Promise((resolve)=> setImmediate(resolve));
}
it("test delay", async()=>{
jest.useFakeTimers();
let response= {statusCode : 200,
JSON.stringify({status:200,data:true})};
callout.mockReturnValueOnce(wait(2000,respone);
jest.advanceTimersByTime(20);
await flushpromises();
expect(callout).not.toHavebeencalled();
jest.advanceTimersByTime(2000);
expect(callout).toHavebeencalled();
});
function wait(ms,value)
{
return new Promise(resolve => setTimeout(resolve,ms,value));
}

Overriding Cypress' before and after methods

We are using Cypress.io to build our automation suite. We have a requirement to seed our database before every test and to clear the data afterward. This could be done like below.
describe('Our test suite', function() {
before(function () {
//loadDbSeed is a custom command that will load the seed file based on the spec file
seed = cy.loadDbSeed()
cy.task('seed:db', seed)
})
it('should be true', function() {
//Some test with some action followed by an assertion
cy.visit('/some-page')
cy.get('[data-cy="identifier"]')
.click()
expect(true).to.equal(true)
})
after(function () {
// clearDb is a custom command that will clear out the DB.
// We are still debating if we must clear the DB after the tests.
// But we might still need to do some common actions for the entire suite after
cy.clearDb()
})
})
The problem we see is that the same before and after operations will be required for all our test suites. So we would like to override these methods so that our tests are something like this.
describe('Our test suite', function() {
before(function () {
// DB seeding is done automatically
// Write only custom before steps required for this test
})
it('should be true', function() {
//Some test with some action followed by an assertion
cy.visit('/some-page')
cy.get('[data-cy="identifier"]')
.click()
expect(true).to.equal(true)
})
after(function () {
// DB clearing is done automatically
// Write only custom after steps required for this test
})
})
How do we achieve this? I have been digging around in the Cypress code and haven't found anything obvious.
If you look at the Cypress docs, using after isn't recommended - Cypress Docs. I'd caution against setting data globally, will you really need it for every test? If you need to enter data on a per-test basis at some point, will that conflict with this global data? You could do something like this on a per test basis:
describe('Our test suite', function() {
beforeEach(function () {
cy.then(async () => {
await MyDatabaseService.resetdata()
await MyDatabaseService.createSomeData()
});
});
it('should be true', function() {
//Some test with some action followed by an assertion
})
})
I've also had to nest some tests as follows when specific tests needed specific data (sorry if some of the formatting here is a bit out, hopefully it'll make sense!):
describe('Our test suite', function() {
beforeEach(function () {
cy.then(async () => {
await MyDatabaseService.resetdata()
await MyDatabaseService.createSomeData()
});
});
it('should be true', function() {
//Some test with some action followed by an assertion
});
describe('These tests need more data, this is a nested describe', function () {
before(function () {
cy.then(async () => {
await MyDatabaseService.addSomeMoreData()
});
it('this test uses the extra data', function () {
// Do some funky tests here
});
});
});
})
For the second tests above, the test will run all three database actions.
Upshot is, if you clear data before you run your tests then it makes things much clearer.
I hope that helps. I'm new to Cypress myself and it can be hard to drop that (bad?) habits we've used for some time in Selenium!

How Test with Jest a function in the method "mounted" VueJS

I would to try call a function already mocked. I use vueJS for the frond and Jest as unit test. Below a example of my code. My purpose is to test the call of « anotherFunction". The first test is succeed , not the second.Thanks for help or suggestion
code vueJS:
mounted() {
this.myfunction();
}
methods: {
myfunction() {
this.anotherFunction();
}
}
Jest code:
describe('Home.vue', () => {
let wrapper = null;
const options = {
mocks: {
$t: () => 'some specific text',
},
methods: {
myFunction: jest.fn(),
},
};
it('Should renders Home Component', () => {
// Given
wrapper = shallowMount(Home, options);
// Then
expect(wrapper).toBeTruthy();
});
it('Should call anotherFunction', async (done) => {
// Given
wrapper.vm.anotherFunction = jest.fn().mockResolvedValue([]);
// When
await wrapper.vm.myFunction();
// THIS THE PROBLEM, myFunction is mocked and I can't call the function 'anotherFunction' inside...
// Then
// expect(wrapper.vm.anotherFunction).toHaveBeenCalled();
});
});
I was finding a good way to help you if this test case. So, I thought in something like the chuck code below:
import { mount } from '#vue/test-utils';
describe('Home', () => {
it('method calls test case', () => {
const anotherMethodMock = jest.fn();
wrapper = mount(Home, {
methods: {
anotherMethod: anotherMethodMock
}
});
expect(anotherMethodMock).toHaveBeenCalled();
});
});
But, the Jest threw the following exception:
[vue-test-utils]: overwriting methods via the methods property is deprecated and will be removed in the next major version. There is no clear migration path for themethods property - Vue does not support arbitrarily replacement of methods, nor should VTU. To stub a complex m ethod extract it from the component and test it in isolation. Otherwise, the suggestion is to rethink those tests.
I had the following insight, maybe, in this case, should be better to test the side effect of this anotherMethod calling. What does it change? Is something being shown to the user?
I believe that here we have started from the wrong concept.
I hope that this tip could be useful :)
As suggested by #Vinícius Alonso, We should avoid using methods and setMethods in our test cases because of it's deprecation. But you can still test the mounted lifecycle by mocking the functions that are being called during mount. So you can do something similar to below snippet.
describe('Mounted Lifecycle', () => {
const mockMethodOne = jest.spyOn(MyComponent.methods, 'methodOne');
const mockMethodTwo = jest.spyOn(MyComponent.methods, 'methodTwo');
it('Validate data and function call during mount', () => {
const wrapper = shallowMount(MyComponent);
expect(mockMethodOne).toHaveBeenCalled();
expect(mockMethodTwo).toHaveBeenCalled();
})
})
Do mount/shallowMount inside it only rather putting it outside of it as it was not working in my case. You can checkout more details on it if you want.

vue-authenticate: Where is the $auth variable defined?

I am trying to implement vue-authenticate, but I'm having an issue with the following example code taken from their documentation:
new Vue({
methods: {
login: function () {
this.$auth.login({ email, password }).then(function () {
// Execute application logic after successful login
})
},
register: function () {
this.$auth.register({ name, email, password }).then(function () {
// Execute application logic after successful registration
})
}
}
})
Where is the $auth property coming from? I can't see it defined anywhere. I've looked through the documentation, and the example code in the repo, but neither provide any help.
As you know vue-authenticate is a Vue-plugin.
And when you use this plugin using the line.
Vue.use(VueAuthenticate, ...data)
And this is where it gets defined in this file
Object.defineProperties(Vue.prototype, {
$auth: {
get() {
if (!vueAuthInstance) {
// Request handler library not found, throw error
if (!this.$http) {
throw new Error('Request handler instance not found')
}
vueAuthInstance = new VueAuthenticate(this.$http, options)
}
return vueAuthInstance
}
}
})
Also you may want to go through this documentation on Adding Instance Properties.

How to broadcast to other controllers when load with module.config or .run in Angularjs

I have a checking when reading the web page,then using the result to refresh sidebar by ng-repeat,but I have errors :
Uncaught Error: Unknown provider: $scope from myModule or
Uncaught Error: Unknown provider: $scope from sharedService
How can I resolve it?
Here is my code
module:
var myModule = angular.module('myModule', []);
service for broadcast:
myModule.factory('mySharedService', function($rootScope) { //service
var sharedService = {};
sharedService.keyHistory = [];
sharedService.linkHistory = [];
sharedService.prepForBroadcast = function(key,link) {
this.keyHistory = key;
this.linkHistory = link;
this.broadcastItem();
};
sharedService.prepForBroadcastAdd =function(key){
console.log(this.keyHistory.push(key));
//this.linkHistory = linkHistory+link;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
config to do Checking:
myModule.config(function($scope,sharedService){
$.ajax({
url:"/fly/AJAX",
type:"POST",
contentType:'application/x-www-form-urlencoded; charset=UTF-8',
datatype:"json",
success:function(data){
if(data!=null){
var loginResult = $.parseJSON(data);
if (loginResult.success == true){
console.log("login success");
$("#userLable").html(loginResult.userName+'('+loginResult.loginID+')');//
if (loginResult.hasHistory==true) {
sharedService.prepForBroadcast(loginResult.searchHistory,[]);
console.log("broadcast");
}
};
}
}
});
});
SideCtrl:
function SideCtrl($scope,sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.keyHistory =sharedService.keyHistory;
$scope.linkHistory = sharedService.linkHistory;
});
}
SideCtrl.$inject = ['$scope', 'mySharedService'];
THX !
The error is due to trying to request a $scope in a config block, which you can't do. If I understand what you're trying to do, then I also think you're over-complicating it. I'd solve the problem a little differently. The details would depend on your requirements and use case, but based on the information you gave...
I'd have a service responsible for communication with the server and storing the state:
app.factory( 'loginService', function ( $http ) {
var result;
function doRequest( data ) {
// just flesh out this post request to suit your needs...
return $http.post( '/fly/ajax', data, {} )
.then( function ( response ) {
// assuming you don't care about the headers, etc.
return response.data;
});
}
// Do it once initially
if ( ! angular.isDefined( result ) ) {
result = doRequest();
}
// return the service's public API
return {
getStatus: function () { return result; },
login: doRequest
};
});
Now the first time this service is requested, the $http request will be made. If you're accessing this from multiple controllers, the post will only occur once because of the isDefined statement. You can then use this in your controllers:
app.controller( 'MainCtrl', function( $scope, loginService ) {
loginService.getStatus().then( function ( data ) {
// do whatever you need to with your data.
// it is only guaranteed to exist as of now, because $http returns a promise
});
});
Every controller accesses it the same way, but it was still only called once! You can set values against the scope and access it from your views, if you want:
app.controller( 'MainCtrl', function( $scope, loginService ) {
loginService.getStatus().then( function ( data ) {
$scope.loginId = data.loginID;
});
});
And in your view:
<h1>Welcome, {{loginId || 'guest'}}!</h1>
And if you need to, you call the function again:
app.controller( 'MainCtrl', function( $scope, loginService ) {
// ...
loginService.login( $scope.user ).then( function ( data ) {
$scope.loginId = data.loginID;
});
// ...
});
As you can see, broadcasting an event is totally unnecessary.
I would do it differently. I would create some sort of more top-level controller, like function MainController($rootScope, $scope, sharedService) and wire it up with body: <body ng-controller='mainController' ng-init='init()'. After that you should create init() method in MainController.
Inside this initialization method I would call sharedService which should make AJAX request (via $http! that's the best practice, and it's very similar to jQuery) and broadcast proper event when required.
That way you make sure to call initialization just once (when MainController is initializing), you stick to the angular's best practices and avoid dodgy looking code.