Slack integration only works on onPrepare, but not in onComplete, afterLaunch? - selenium

I am hooking my protractor tests with Slack, the idea is to send a message on a slack room if the test started and once it's done.
However, I can only successfully send messages to slack during onPrepare. But not in afterLanch or even in onComplete.
I tried to do a simple console.log in the afterLaunch and onComplete so I know it works. The only thing that's confusing me is why it does not send the message in slack AFTER the test is done.
If there is a better way of doing this, please tell me your ideas. Right now, this is the best i've got.
Please see my code:
let SpecReporter = require('jasmine-spec-reporter').SpecReporter;
var webRep = require('jasmine-slack-reporter');
var Jasmine2HtmlReporter = require('protractor-jasmine2-html-reporter');
var SlackWebhook = require('slack-webhook');
var slack = new SlackWebhook('my-webhook-url');
exports.config = {
capabilities: {
'browserName' : 'chrome',
},
seleniumAddress: 'http://localhost:4446/wd/hub',
specs: ['./smoke-test/loginAccountType.js'],
onPrepare: function () {
browser.ignoreSynchronization = true,
slack.send({
text: "Test is starting",
channel: '#test-report'
}),
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: true
}
})),
jasmine.getEnv().addReporter(
new Jasmine2HtmlReporter({
savePath: './reports',
takeScreenshotsOnlyOnFailures: true,
})
);
},
afterLaunch: function () {
slack.send({
text: "Test is Done",
channel: '#test-report'
})
},
jasmineNodeOpts: {
// Default time to wait in ms before a test fails.
defaultTimeoutInterval: 100000,
print: function() {},
},
};

It seems like your slack.send() is async?
Try to return promise from your afterLanch/onComplete - in this case protractor will wait for this promise to resolve before killing node.js process.
I never worked with slack-webhook package. But what i see in documentation - .send() is returning promise. Try to return it from your afterLaunch/onComplete:
afterLaunch: function () {
return slack.send({
text: "Test is Done",
channel: '#test-report'
})
},

In addition to #Xotabu4 his answer.
According to the docs the send is async, so do a return;
But also catch the error, just to check what fails, see below.
return slack.send('some text')
.then(function(res) {
// succesful request
})
.catch(function(err) {
// handle request error
})
Hope it helps

Related

Protractor - How to use tags on Jasmine for e2e testning

I'm having some issues where I am stuck on what to do to be able to tag different tests that I want to run.
The tests that I want is only required at the end where I want to be able to tag which payment I would like to pay which is Mastercard, Visa or Amex. I do have a test that does the details page such as writing users information, choose shipment but then at the end I do have multiply options that I would like to test pending on what I want to test:
paymentPage.js
describe('Payment page', function () {
paymentPage = new PaymentPage();
// The details page is accessible by the specified URL
it(`Credit Card - Has a form that can receive user data`, async function () {
await paymentPage.creditCardPayment();
});
//Visa
it('Enets payment', async function () {
//TODO
});
//Amex
it('Enets payment', async function () {
//TODO
});
});
As you can see there is 3 options that I would like to test so whenever I do etc "protractor e2e run mastercard" so that means it should run the first test case, if I choose visa then do the second test case and skip the rest basically.
However I do have couple of tests that executes before coming to the payment page but they all need to do the same for each payment so meaning that all test cases before payment needs to do exactly the same thing everytime (So I guess we just need to do tags at the payment to let the script know which payment to choose)?
How can I do a sort of tagging or maybe someone has another solution that is better? That I can be able to choose what payment provider I want to run
edit:
exports.config = {
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: [
'incognito', 'disable-extensions', 'start-maximized', 'disable-infobars', '--window-size=1920,1080'
]
},
loggingPrefs: { browser: 'ALL' },
platform: 'ANY',
version: ''
},
specs: [
'pagesDesktop/testPage.js',
'pagesDesktop/paymentPage.js'
],
jasmineNodeOpts: {
reporter: "mochawesome",
defaultTimeoutInterval: 60000
},
SELENIUM_PROMISE_MANAGER: false,
framework: 'jasmine',
params: {
cardType: {
}
}
};
describe('Payment page', function () {
paymentPage = new PaymentPage();
console.log(browser.params.cardType);
if (browser.params.cardType === "mastercard") {
// The details page is accessible by the specified URL
it(`Credit Card - Has a form that can receive user data`, async function () {
await paymentPage.creditCardPayment();
});
}
just add if/else logic
describe('Payment page', function () {
paymentPage = new PaymentPage();
// The details page is accessible by the specified URL
it(`Credit Card - Has a form that can receive user data`, async function () {
await paymentPage.creditCardPayment();
});
if (browser.params.cardType === 'visa') {
it('Enets payment', async function () {
//TODO
});
} else if (browser.params.cardType === 'amex') {
//Amex
it('Enets payment', async function () {
//TODO
});
}
});
You can read how to parameterize teste here How can I use command line arguments in Angularjs Protractor?, or here
https://medium.com/#nicklee1/making-your-protractor-tests-data-driven-e3c9e2a5e4e7

RTCPeerConnection.createAnswer callback returns undefined object in mozilla for WebRTC chat

Following is my code to answer the incoming call:
var pc = connection.pc;
pc.setRemoteDescription(sdp,function() {
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer,function() {
// code for sending the answer
})
})
})
The above code works fine for chrome, but when i run the same in mozilla, the answer obtained from pc.createAnswer callback is undefined. As a result of which it gives me following error:
TypeError: Argument 1 of RTCPeerConnection.setLocalDescription is not
an object.
The problem is you're not checking errors, specifically: not passing in the required error callbacks.
setRemoteDescription and setRemoteDescription require either three arguments (legacy callback style) or one (promises), but you're passing in two. Same for createAnswer minus one.
The browser's JS bindings end up picking the wrong overload, returning you a promise which you're not checking either, effectively swallowing errors.
Either add the necessary error callbacks:
var pc = connection.pc;
pc.setRemoteDescription(sdp, function() {
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer, function() {
// code for sending the answer
}, function(e) {
console.error(e);
});
}, function(e) {
console.error(e);
});
}, function(e) {
console.error(e);
});
Or use the modern promise API:
var pc = connection.pc;
pc.setRemoteDescription(sdp)
.then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(() => {
// code for sending the answer
})
.catch(e => console.error(e));
The promise API is available natively in Firefox, or through adapter.js in Chrome. See fiddle.
And always check for errors. ;)

chai spy passes to.be.spy assertion, but throws error on .called() "is not a spy or a call to a spy"

I am attempting to use chai spies in my unit tests. I am using karma, mocha, chai and sinon.
I was originally trying to use chai spies to verify a callback in my angular app was called when provided. But to solve the error I have boiled my test case down to what I think is a pretty simple one.
I have the below unit test
describe('spy tests:', function() {
it('should be spy', function() {
var spy = chai.spy();
expect(spy).to.be.spy;
});
it('should have been called', function() {
var spy = chai.spy();
spy();
expect(spy).to.have.been.called();
});
}
The first "should be spy" test passes, which as far as I can reason means that a spy is in fact being created. However the second test fails with the below error:
TypeError: { [Function]
toString: { [Function: toString] bind: { [Function: bind] bind: [Circular] } },
reset: { [Function] bind: { [Function: bind] bind: [Circular] } },
__spy: { calls: [ [] ], called: true, name: undefined },
bind: { [Function: bind] bind: [Circular] } } is not a spy or a call to a spy!
This is particularly frustrating as I just verified it is a spy in the previous "should be spy" assertion.
Below are my frameworks, as I am including them in my karma.conf.js:
frameworks: ['chai-as-promised', 'chai-things', 'chai-spies', 'sinon-chai', 'chai', 'mocha']
To make matters more frustrating the below assertion does pass:
expect(spy.__spy.called).to.be.true;
I am happy to provide any other info needed. Thanks!
I'm not a chai spies expert, but I was having similar issues getting chai spy to work.
I just tested the function, and there appears to be a missing ")" bracket at the end; Maybe it's a copy and paste error?
describe('spy tests:', function() {
it('should be spy', function() {
var spy = chai.spy();
expect(spy).to.be.spy;
});
it('should have been called', function() {
var spy = chai.spy();
spy();
expect(spy).to.have.been.called();
});
});
If that's not the issue, then I was able to create a successful chai spy test by overwriting my function variable with spy.
function functionA() {
}
function thatCallsFunctionA() {
functionA();
}
describe('Function thatCallsFunctionA', function() {
it('Should call functionA', function() {
var functionA = spy(functionA);
thatCallsFunctionA();
expect(functionA).to.have.been.called();
});
})

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

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.

Why success callback is not called in extjs form submission?

I'm trying to upload a file using Ext JS forms and in case of success or failure, show appropriate messages. But I'm not able to get the desired result. I'm not able to make success or failure callbacks work in form.submit action.
What I've done till now is:
Creating a form with this script:
new Ext.FormPanel({
fileUpload: true,
frame: true,
url: '/profiler/certificate/update',
success: function() {
console.log(arguments);
},
failure: function() {
console.log(arguments);
}
}).getForm().submit()
​/*
The response Content-Type is text/html (with charcode=utf8);
The response JSON is: { "success": true }
*/​​
Setting the response Content-Type to text/html based on this answer.
Sending an appropriate JSON result back, based on Ext JS docs. The response captured via Fiddler is:
{"success":false}
or
{"success":true}
I even set the response Content-Type to application/json. But still no success.
I've read links like this and this, but none of them helped. Please note that I also tried another script which creates a form, with an upload field in it, and a save button, and I submitted the form in the handler of the save button. But still no callback is fired.
Here's a working example - Javascript code:
Ext.onReady(function () {
Ext.define('ImagePanel', {
extend: 'Ext.form.Panel',
fileUpload: true,
title: 'Upload Panel',
width: 300,
height: 100,
onUpload: function () {
this.getForm().submit({
url: 'upload.php',
scope: this,
success: function (formPanel, action) {
var data = Ext.decode(action.response.responseText);
alert("Success: " + data.msg);
},
failure: function (formPanel, action) {
var data = Ext.decode(action.response.responseText);
alert("Failure: " + data.msg);
}
});
},
initComponent: function () {
var config = {
items: [
{
xtype: 'fileuploadfield',
buttonText: 'Upload',
name: 'uploadedFile',
listeners: {
'change': {
scope: this,
fn: function (field, e) {
this.onUpload();
}
}
}
}
]
};
Ext.apply(this, Ext.apply(this.initialConfig, config));
this.callParent(arguments);
}
});
var panel = Ext.create('ImagePanel', {
renderTo: Ext.getBody()
});
});
And PHP code:
<?php
if (isset($_FILES)) {
$temp_file_name = $_FILES['uploadedFile']['tmp_name'];
$original_file_name = $_FILES['uploadedFile']['name'];
echo '{"success": true, "msg": "'.$original_file_name.'"}';
} else {
echo '{"success": false, "msg": "No Files"}';
}
I have been struggling with this for quite some time now as well. Here's my code:
Ext.getCmp('media-upload-form').getForm().doAction('submit', {
url: './services/recordmedia/upload',
method: 'post',
waitMsg: 'Please wait...',
params: {
entityId: this.entityId,
},
failure: function(form, action){
alert(_('Error uploading file'));
this.fireEvent('file-upload');
this.close();
},
success: function(form, action){
this.fireEvent('file-upload');
this.close();
},
scope: this
})
The response was always wrapped in <pre> tags by the browser, what caused the Extj lib not to call the callbacks. To fix this:
make sure your server returns the correct json: {"success":true}
make sure that the content-type is set to text/html
Actually, this is well covered by docs for Ext.form.Panel and Ext.form.Basic. The problem with your code not working is that there are no config options "success", "failure" for the form panel. You should put them in the config object passed to the submit action. So your code should look like:
new Ext.FormPanel({
fileUpload: true,
frame: true
}).getForm().submit({
url: '/profiler/certificate/update',
success: function() {
console.log(arguments);
},
failure: function() {
console.log(arguments);
}
});
Note the difference: In Ext 4, there is a form component (Ext.form.Panel) which is basically a view component concerned with how you form looks, and then there is the underlying form class (e.g. Ext.form.Basic) concerned with the functionality. Form submissions are handled by Ext.form.Basic (or whatever returned by your form.getForm()).