Protractor element.click() throwing an exception - protractor

I was trying to figure out why .click() below was crashing protractor :
this.clickSecondPanel = function () {
element(by.css('div.panels-gs.panel-top-two-gs')).click();
}
until I changed the line to :
element(by.css('div.panels-gs.panel-top-two-gs')).click;
where my spec.js looks something like :
var DataCardPage = require('./pageObjects/dataCard.page.js');
var dataCardPage = new DataCardPage();
describe('Clicking on the 2nd panel', function () {
dataCardPage.clickSecondPanel();
it('Should select the 2nd test panel', function () {
expect(dataCardPage.getSecondPanelText()).toBe('TEST123');
});
In other places in my code, I use .click() (with parenths), so this is confusing to me.
The error is nasty:
Started
[17:44:23] E/launcher - Error while waiting for Protractor to sync with the page
: "window.angular is undefined. This could be either because this is a non-angu
lar page or because your test involves client-side navigation, which can interfe
re with Protractor's bootstrapping. See http://git.io/v4gXM for details"
Any advice appreciated...
Bob

Solved this in the comments above, posting as an answer.
My suggestion was to try moving the clickSecondPanel() inside the it block. It looked suspicious by itself just from a "best practice" perspective as I do not have any code that is outside of a jasmine function i.e. it, beforeAll, afterAll etc (don't even know where I learned that habit honestly).
It also seemed to effect the control flow and asynchronous execution so the click() event was triggering too soon. This can be explained in part by this documentation and/or this blog post

Try using browser.ignoreSynchronization=true at the begining of your test. May be the application that you are trying to automated does not contain angular in it.

Related

Stop huge error output from testing-library

I love testing-library, have used it a lot in a React project, and I'm trying to use it in an Angular project now - but I've always struggled with the enormous error output, including the HTML text of the render. Not only is this not usually helpful (I couldn't find an element, here's the HTML where it isn't); but it gets truncated, often before the interesting line if you're running in debug mode.
I simply added it as a library alongside the standard Angular Karma+Jasmine setup.
I'm sure you could say the components I'm testing are too large if the HTML output causes my console window to spool for ages, but I have a lot of integration tests in Protractor, and they are SO SLOW :(.
I would say the best solution would be to use the configure method and pass a custom function for getElementError which does what you want.
You can read about configuration here: https://testing-library.com/docs/dom-testing-library/api-configuration
An example of this might look like:
configure({
getElementError: (message: string, container) => {
const error = new Error(message);
error.name = 'TestingLibraryElementError';
error.stack = null;
return error;
},
});
You can then put this in any single test file or use Jest's setupFiles or setupFilesAfterEnv config options to have it run globally.
I am assuming you running jest with rtl in your project.
I personally wouldn't turn it off as it's there to help us, but everyone has a way so if you have your reasons, then fair enough.
1. If you want to disable errors for a specific test, you can mock the console.error.
it('disable error example', () => {
const errorObject = console.error; //store the state of the object
console.error = jest.fn(); // mock the object
// code
//assertion (expect)
console.error = errorObject; // assign it back so you can use it in the next test
});
2. If you want to silence it for all the test, you could use the jest --silent CLI option. Check the docs
The above might even disable the DOM printing that is done by rtl, I am not sure as I haven't tried this, but if you look at the docs I linked, it says
"Prevent tests from printing messages through the console."
Now you almost certainly have everything disabled except the DOM recommendations if the above doesn't work. On that case you might look into react-testing-library's source code and find out what is used for those print statements. Is it a console.log? is it a console.warn? When you got that, just mock it out like option 1 above.
UPDATE
After some digging, I found out that all testing-library DOM printing is built on prettyDOM();
While prettyDOM() can't be disabled you can limit the number of lines to 0, and that would just give you the error message and three dots ... below the message.
Here is an example printout, I messed around with:
TestingLibraryElementError: Unable to find an element with the text: Hello ther. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
...
All you need to do is to pass in an environment variable before executing your test suite, so for example with an npm script it would look like:
DEBUG_PRINT_LIMIT=0 npm run test
Here is the doc
UPDATE 2:
As per the OP's FR on github this can also be achieved without injecting in a global variable to limit the PrettyDOM line output (in case if it's used elsewhere). The getElementError config option need to be changed:
dom-testing-library/src/config.js
// called when getBy* queries fail. (message, container) => Error
getElementError(message, container) {
const error = new Error(
[message, prettyDOM(container)].filter(Boolean).join('\n\n'),
)
error.name = 'TestingLibraryElementError'
return error
},
The callstack can also be removed
You can change how the message is built by setting the DOM testing library message building function with config. In my Angular project I added this to test.js:
configure({
getElementError: (message: string, container) => {
const error = new Error(message);
error.name = 'TestingLibraryElementError';
error.stack = null;
return error;
},
});
This was answered here: https://github.com/testing-library/dom-testing-library/issues/773 by https://github.com/wyze.

Office JavaScript API: selecting a range in Word for Mac

I'm working on a side project using the Microsoft Office JavaScript APIs. I have some functionality working to select a range in order to scroll to a particular position within a document. This works as expected in Office for the web, but in Office for Mac I get the following error when calling context.sync().then():
Unhandled Promise Rejection: RichApi.Error: ItemNotFound
I can't find any documentation on that particular error, and I'm not sure how to troubleshoot what I might be doing wrong. What am I missing? Like I said, this works in the web interface.
Here is minimal sample of code that demonstrates the problem:
function UI(context) {
this.context = context;
}
UI.prototype.initialize = function() {
var paragraphs = this.context.document.body.paragraphs;
this.context.load(paragraphs);
document.querySelector('button').addEventListener('click', () => {
this.context.sync().then(() => {
this.goToRange(paragraphs.items[0]);
});
});
};
UI.prototype.goToRange = function(range) {
range.select();
this.context.sync();
};
document.addEventListener('DOMContentLoaded', () => {
Office.onReady(() => {
Word.run(context => {
return context.sync().then(() => {
new UI(context).initialize();
});
});
});
});
The only thing I can think of is that maybe the reference to the paragraph client object becomes "stale" in some sense, perhaps based on some resource limits that are lower in the Mac application than in the online interface? (That would be counterintuitive to me, but it's the only thing that comes to mind.)
I think I figured out the problem. I stumbled upon a hint while putting together the minimum code sample in the question; I removed a little too much code at first and encountered the following error:
Unhandled Promise Rejection: RichApi.Error: The batch function passed
to the ".run" method didn't return a promise. The function must return
a promise, so that any automatically-tracked objects can be released
at the completion of the batch operation.
I believe the issue is that, at least in Word for Mac, you can't use the context object provided by Word.run in an asynchronous event listener. I'm guessing this is because, as the above error states, some state has been released after resolving the promise returned. I can get the code to work by adding a dedicated call to Word.run (and using the fresh context provided) inside the event listener.
It is still a little odd that it works just fine in the browser. Presumably, the same state is not released as aggressively in the browser-based version.

Taking webpage screenshot on completion of a Cucumber Step Definition

I'm currently researching a way in which I can implement a screen capture method in my acceptance test suite in Scala Cucumber after each step definition is completed in the scenario.
I have already implemented a method that will take a screenshot of a webpage if one of the automation test fails by invoking the method in the after hooks class. This does work fine but this will only capture the web page once the entire scenario has been completed.
I wasn't sure if there was something like before and after hooks that could be applied to the steps instead of the scenario.
Hooks.scala
#After
def tearDown(result: Scenario){
if (result.isFailed) {
ifCurrentDriverTakesSnapshot {
takesSnapshot =>
Snapshotter.takeErrorSnapshot(takesSnapshot, result)
}
}
Snapshotter.Scala
def takeErrorSnapshot(takesScreenshot: TakesScreenshot, result: Scenario)
= {
try {
val screenshot = takesScreenshot.getScreenshotAs(OutputType.BYTES)
result.embed(screenshot, "image/png")
}
catch {
case e: WebDriverException =>
e.printStackTrace(System.err)
}
}
I would like to be able to do this in a class or method that can be called after each new page is opened or after each step definition. I could right a step definition that would handle the screen capture but I would like to do in a better way as I have 100's of test scenarios so it would be better to avoid adding a step for this in between every step definition as they have done in the below link.
Cucumber Java screenshots
If anyone could share some light on the matter i'd greatly appreciate it as I'm struggling to find much on the subject.
Thanks!!!

Protractor + ionicPopup

Has anyone succeeded in using Protractor to detect an ionicPopup alert?
I've tried all the workarounds suggested here but no luck.
I need Protractor to detect the alert and check the text in the alert.
Here's the class I wrote to test that the popup exists and to ensure the text is correct in the header and body:
var TestUtilities = function(){
this.popup = element(by.css('.popup-container.popup-showing.active'));
//Tests to see if $ionicPopup.alert exists
this.popupShouldExist = function() {
expect(this.popup.isDisplayed()).toBeTruthy();
};
//Tests to see if $ionicPopup.alert contains the text provided in the argument exists in the header
this.popupContainsHeaderText = function (text) {
this.popupShouldExist();
expect(this.popup.element(by.css('.popup-head')).getText()).toMatch(text);
};
//Tests to see if $ionicPopup.alert contains the text provided in the argument exists in the body
this.popupContainsText = function (text) {
this.popupShouldExist();
expect(this.popup.element(by.css('.popup-body')).getText()).toMatch(text);
};
};
module.exports=TestUtilities;
Also check out this site for more on testing Ionic in protractor it talks about how to check to see if the popup exists: http://gonehybrid.com/how-to-write-automated-tests-for-your-ionic-app-part-3/
I've tested Ionic popups successfully by setting the popup variable as follows:
var popup = element(by.css('.popup-container.popup-showing.active'));
And in the test:
expect(popup.isDisplayed()).toBeTruthy();
Ionic Popups are just made of DOM elements, so you should be able to use normal locators to find/test them. Because they're not made of alerts, the workarounds in the issue you linked to are probably not useful.
I got it - I saw a lot of issues out there trying to do it in very complex ways, but in the end I tried this and it turns out to be this simple.
Inspect your element and find its ng-repeat value, then
var button = element(by.repeater('button in buttons')).getText()
You also need to have the browser sit out somehow for a couple seconds so it doesn't resolve to the tests while the ionic popup isn't actually there.
For that, browser.sleep(3000);
That's it! However, getting the other button in there is proving to be a little problem. var button = element(by.repeater('button in buttons')).get(0) or .get(1) return undefined is not a function.
Please accept the answer if you like it! If I figure out how to get the other button, I'll post it here.

tinymce 3.5.4.1 adding event handlers, lots of errors

I'm adding a TinyMCE editor to one of our pages. For reasons above my pay-grade, we're using TinyMCE 3.5.4.1.
It's gone pretty well, except when I try to wire in an onFocus event handler. If I don't try to use the handler, the editors show up and work fine but on pages where I define a handler, there are boatloads of exceptions inside TinyMCE for the browser types I've tried (IE8,9,10 and Chrome).
I've been using templates from other posts I've seen on here but I haven't seen mention of all these TinyMCE exceptions. Of course, trying to unwind the minified script is a real pain.
The first example I found here had a setup function in the TinyMCE config like this:
, setup: function (ed) {
ed.onInit.add(function (ed, evt) {
if (!myFocus) return; // global for the handler to use
var dom = ed.dom;
var doc = ed.getDoc();
tinymce.dom.Event.add(doc, 'focus', myFocus);
});
}
When myFocus is defined, there are a number of exceptions TinyMCE throws starting with
if (j.isIE){l.attachEvent(...)} complaining that l.attachEvent doesn't exist. Then it moves on to all kinds of variable type mismatches.
Chrome developer tools are much more awkward fiddling with minified code, so I'm not sure what all it doesn't like.
Another post I found here suggested doing some minimal browser detection but this helped neither IE nor Chrome.
var doc = s.content_editable ? ed.getBody() : (tinymce.isGecko ? ed.getDoc() : ed.getWin())
Another post suggested a different approach, but I still had all the same errors in both browsers.
, setup: function (ed) {
ed.onInit.add(function (ed, evt) {
if (!myFocus) return;
ed.onFocus.add(myFocus);
});
}
I've also just tried (in vain)
, setup: function (ed) {
if (!myFocus) return;
ed.onFocus.add(myFocus);
}
Is event handling in TinyMCE just very fragile? Not well supported across browsers? Should I just steer clear of it and try using jQuery or something else?
Thanks
Mark
Turns out to be a pilot-error bug being deferred until the extend calls created the editor.