Convenient logging with protractor - selenium

I'm trying to make logging easier for devs writing selenium tests with protractor.
I'm looking at selenium-webdriver/lib/logging and am trying to figure out how to make a convenient logging system.
Here is an example spec:
it('should NOT show welcome before login', () => {
// convenient log here
expect(homepage.logo.isPresent()).toBe(true);
// log message that would occur after expect
expect(homepage.welcomeText.isPresent()).toBe(false);
// final log message
});
I'm not quite sure how to go about this.
I'm trying to avoid having to do (below) for every log message.
homepage.welcomeText.isPresent().then(() => console.log('foo bar'));

There is a npm package - log4js-protractor-appender which will solve your problem.It is built specially for Protractor based environments and it places all logger command in Protractor Control flow and resolves Protractor promises before logging.
Since Protractor executes all commands in a Control Flow , and all non protractor commands dont get executed in the order we like. So regular logging will need an extra effort from us to chain a non-protractor command to a protractor command
Example:
browser.getCurrentUrl().then(function _logValue(url){
logger.info("The url is" + url);
});
But log4js-protractor-appender enabled to write something like this directly - browser.logger.info('Displayed text is:', browser.getCurrentUrl());
For more details on how to implement this- Please check my blog post - How to implements logs for Protractor/JavaScript based Test Automation Frameworks

For expects you can use toBeTruthy or Falsy and include message there. It would log if something goes wrong. Page Object pattern says you must not have weddriver methods in spec files meaning you may cretae method which would verify something present or not and then() log there like in your example. Also you can implement asyncLog function. console.log() method goes to Stack and executes before protractor methods since protractor's Control Flow or Managed Promise. It wraps every protractor method in deffered promise which puts it in callback queue which executes only after stack is empty. Take a look at next code. I didn't try it out for Protractor though but you can get the idea.
var promise = Promise.resolve();
function asyncLog(message) {
Promise.resolve().then(() => console.log(message));
}
console.log('Start');
promise
.then(() => console.log('This is then'))
asyncLog('This is Callback Queue log');
console.log('This is Call Stack log');
promise
.then(() => console.log('This is another then'))

Related

Should I close page in puppetter cluster task closure when using long lasting cluster

I have a cluster for which I have defined a task. As per example in the README.md, I have a closure which accepts a page instance as an argument. I navigate to the page and capture a screenshot. I don't do anything else with the page instance. In the README.md example, there's an await for idle event and then the cluster is closed. However I have a cluster which I virtually never want to close. Should I change the behaviour of my closure in that scenario to close the page?
I suspect I have got a memory leak somewhere in my service and one of the causes I am investigating is whether the cluster closes pages after I am done using them. I use concurrency: Cluster.CONCURRENCY_CONTEXT option.
await puppeteer.task(async ({ page }) => {
// ... my screenshot logic
// do I need to do this?
await page.close();
});

How would you redirect calls to the top object in Cypress?

In my application code, there are a lot of calls (like 100+) to the "top object" referring to window.top such as top.$("title") and so forth. Now, I've run into the problem using Cypress to perform end-to-end testing. When trying to log into the application, there are some calls to top.$(...) but the DevTools shows a Uncaught TypeError: top.$ is not a function. This resulted in my team and I discovering that the "top" our application is trying to reach is the Cypress environment itself.
The things I've tried before coming here are:
1) Trying to stub the window.top with the window object referencing our app. This resulted in us being told window.top is a read-only object.
2) Researching if Cypress has some kind of configuration that would smartly redirect calls to top in our code to be the top-most environment within our app. We figured we probably weren't the only ones coming across this issue.
If there were articles, I couldn't find any, so I came to ask if there was a way to do that, or if anyone would know of an alternate solution?
Another solution we considered: Looking into naming window objects so we can reference them by name instead of "window" or "top". If there isn't a way to do what I'm trying to do through Cypress, I think we're willing to do this as a last resort, but hopefully, we don't have to change that, since we're not sure how much of the app it will break upfront.
#Mikkel Not really sure what code I can provide to be useful, but here's the code that causes Cypress to throw the uncaught exception
if (sample_condition) {
top.$('title').text(...).find('content') // Our iframe
} else {
top.$('title').text(page_title)
}
And there are more instances in our code where we access the top object, but they are generally similar. We found out the root cause of the issue is that within Cypress calls to "top" actually interface with Cypress instead of their intended environment which is our app.
This may not be a direct answer to your question, it's just expanding on your request for more information about the technique that I used to pass info from one script to another. I tried to do it within the same script without success - basically because the async nature of .then() stopped it from working.
This snippet is where I read a couple of id's from sessionStorage, and save them to a json file.
//
// At this point the cart is set up, and in sessionStorage
// So we save the details to a fixtures file, which is read
// by another test script (e2e-purchase.js)
//
cy.window().then(window => {
const contents = {
memberId: window.sessionStorage.getItem('memberId'),
cartId: window.sessionStorage.getItem('mycart')
}
cy.writeFile(`tests/cypress/fixtures/cart.json`, contents)
})
In another script, it loads the file as a fixture (fixtures/cart.json) to pull in a couple of id's
cy.fixture(`cart`).then(cart => {
cy.visit(`/${cart.memberId}/${cart.cartId}`)
})

How to see if nock is matching the request or not?

How to see if nock is matching the request or not? Is there a way one can log information on console regarding nock is matching or not to the requests being made?
The log function is no more available on newer versions of nock e.g. v13.0.0 but you can set the DEBUG environment variable to nock.* as described here https://github.com/nock/nock#debugging to log each step of the matching process. For example:
export DEBUG=nock.* && yarn test
if you're using yarn to run your tests.
It is very simple.
Just add .log(console.log) to your nock object!
nock('https://github.com')
.log(console.log)
.get('/')
Basically, nock checks all the interceptors it has active until a match is found for your request (in case you have multiple nock interceptors mocking a variety of requests). So what will be logged using .log(console.log) is,
a whole url to which the request is being made.
a url from the interceptor
true or false, depending upon the match scenario.
Hope this will help you resolve mysteries of nock :)
You can log requests to the console using:
nock.recorder.rec();
You can also listen to nock's event emitters to create a callback for whenever a request is intercepted. For example:
const requestCallback = () => {
console.log('Request was called');
}
nock('https://example.org')
.persist()
.get('/')
.on('request', requestCallback);

How can I make browserify onComplete handlers wait for my plugin to finish?

I'm writing a browserify plugin which adds a transform but also needs to do things at the very end, once browserify has finished going through the require tree. Achieving this is simple since my plugin receives a browserify instance (which I call brow), so I can just brow.transform(...) to add my transform and use the following to do more at the end:
brow.on('bundle', function (bundler) {
bundler.on('end', doMoreAtTheEnd)
})
This works fine except for one pretty important detail. Any other handlers expected to run when browserify is done (via the onComplete handler brow.bundle(onComplete)) execute too soon (before my plugin is done doing it's work). This makes testing my plugin rather difficult, for example, since the assertions I wrote in the onComplete handler execute before my plugin is done doing its work.
I understand why this happens: my plugin and my assertions are both using the same onComplete event, so they will run one immediately before the other, and since my plugin is doing async work, the assertions run too soon). but I'm not sure how to achieve what I want without extreme hackery... I could use setTimeout, of course, but I'm trying to avoid that, for reasons that should be obvious...
Any ideas?
update
I've reduced my problem to a short gist, if that helps
I found a solution using the browserify pipeline. The trick is to use the flush callback of the last phase in the pipeline (wrap).
var through = require('through2')
var wrap = brow.pipeline.get('wrap')
wrap.push(through(function (buf, enc, next) {
// This leaves the chunks unchanged
this.push(buf)
next()
}, function (next) {
// Here I can do some async work
setTimeout(function () {
// and call `next` when I'm done
next()
}, 1000)
})
I made a new gist which applies this fix to the original gist illustrating the problem in the question.

soda/selenium how to log an assert

I'm sort of new to selenium and soda but love the ability to run selenium with node. So bear with me as I ask a really basic question.
This is a simple login chain I'm running in my script.
browser
.click('css=a#loginButton')
.type('css=input.input-medium.email',user)
.type('css=input.input.pwd',pass)
.clickAndWait('css=a.btn.login')
.asserTextPresent('Clients','logged in ok') //client page reached
however the message 'logged in ok' is not spitting out anywhere. I'm certain this is a basic thing I'm missing here but there has to be a way to spit out progress messages as I'm running my test script. This script has become HUGE, so it's not very easy to track down errors when they occur. I'd like to sort of log the success as well as the errors.
DOH! It's a function not a string.
For anyone else wondering. Here's the solution:
browser
.click('css=a#loginButton')
.type('css=input.input-medium.email',user)
.type('css=input.input.pwd',pass)
.clickAndWait('css=a.btn.login')
.assertTextPresent(
'Clients',
function () {
console.log('logged in ok')
}); //client page reached