document.evaluate won't work from content script - javascript

var allTags = document.evaluate("//*[contains(#src,'"+imgSrc+"')]", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
This is the code that gives errors, it gives:
Uncaught Error: TYPE_ERR: DOM XPath Exception 52
Could someone tell me what the problem is?

I don't have a precise answer, but I can guess and give a workaround.
First the work around: change UNORDERED_NODE_SNAPSHOT_TYPE to a type that don't create a snapshot(unless you need it that way) and returns multiple nodes like UNORDERED_NODE_ITERATOR_TYPE(or ANY_TYPE).
And my guess: After reading the spec it say for this function 'TYPE_ERR: Raised if the result cannot be converted to return the specified type.'. It may be the case it can't allocate the resources to create a snapshot or something like this(the workaround assumes that).
Edit:
The real problem is most likely not the call to document.evaluate is that in your code you do allTags.iterateNext and this call expects allTags to be a *_NODE_ITERATOR_TYPE and not a *_NODE_SNAPSHOT_TYPE, using allTags.snapshotItem don't cause an error to be thrown. I wrote a sample at jsfiddle, it changes the borders after 2 seconds using the call to evaluate in your question and iterate over the elements in the proper way.

Related

Uncaught TypeError: Cannot read property 'children' of null while running JavaScript in Chrome Console

I got this error while executing my JS code in chrome console
Uncaught TypeError: Cannot read property 'children' of null
at getFollowers (<anonymous>:98:53)
at <anonymous>:57:7
My Function being
function getFollowers(){
var followersDiv = document.querySelector('div[role="presentation"]').querySelector('li').parentElement.children;
for (var i = 0;i<followersDiv.length;i++){
var tempUser = followersDiv[i].lastElementChild.children[0].children[1];
followers.push({
userID: tempUser.children[0].textContent,
userName: tempUser.children[1].textContent,
});
}
document.querySelector('[aria-label="Close"]').click();
}
The line seems to have missing something here
var tempUser = followersDiv[i].lastElementChild.children[0].children[1];
Can someone help me with this?
I would’n say that the line you’re pointing is missing something. I would say this given line expects things to be here without providing enough guarantees.
One way or another, tempUser is built trying to use something that can be null. In my opinion, your code is quite optimistic : you assume lastElementChild to be nonEmpty, to have a children field which is a non empty array, etc… Each step after "followersDiv[i]" may lead to failure, you did not check the values’ content.
There are two strategies to make your code more robust, IMO :
Use an external library like lodash to have a more robust code. In particular, you may use _.get and _.isEmpty
Try to use a more accurate CSS selector in your querySelector as I’m not sure you need to manually browse the DOM if your selector is good enough. This way, you won’t have nodes that don’t fit your expectations.

innerHTML and outerHTML property on HTMLElement return undefined with no error

Goal: Take a HTML as string, edit some attributes of image tags in it, return the HTML as string.
My function is along following lines:
private resolveImagesInHTML (body: string): string {
let htmlParser = new domParser.DOMParser(); // from react-native-html-parser
let parsedDOM: HTMLDocument = htmlParser.parseFromString('<html>' + body + '</html>', 'text/html');
// ------- code to modify DOM goes here -------
return parsedDOM.documentElement.innerHTML;
}
Issue: If I print parsedDOM or parsedDOM.documentElement in my debug console,
it correctly displays the modified HTML content. Surprisingly,
innerHTML or outerHTML property throws no error but returns
'undefined'.
My obvious guess was that I might have messed up the format of HTML while modifying my code but even if I comment everything inside my function except the three lines above, the behavior is still the same.
What could I possibly be doing wrong here?
Thanks, I am pretty new to React-Native and typescript so I won't be surprised if I am missing some trivial and obvious thing here.
htmlParser.parseFromString
returns something that looks like the DOM, that has some methods in common with the DOM but not all of them. It does not have .innerHTML. Real browser DOM have it.
From what is written here https://www.npmjs.com/package/react-native-html-parser innerHTML is not available
So why does it not throw an Error ? Because that is how JavaScript Objects work.
console.log({}.innerHTML) // No Error
How to force an error ?
There are 2 solutions, One is to use strict mode and enable strict mode warnings in the console. The other is wrap things with Proxys that checks for hasOwnProperty for example.

Understanding which line break to target for replace() [duplicate]

I have a very specific problem concerning a regular expression matching in Javascript. I'm trying to match a piece of source code, more specifically a portion here:
<TD WIDTH=100% ALIGN=right>World Boards | Olympa - Trade | <b>Bump when Yasir...</b></TD>
The part I'm trying to match is boardid=106121">Olympa - Trade</a>, the part I actually need is "Olympa". So I use the following line of JS code to get a match and have "Olympa" returned:
var world = document.documentElement.innerHTML.match('/boardid=[0-9]+">([A-Z][a-z]+)( - Trade){0,1}<\/a>/i')[1];
the ( - Trade) part is optional in my problem, hence the {0,1} in the regex.
There's also no easier way to narrow down the code by e.g. getElementsByTagName, so searching the complete source code is my only option.
Now here's the funny thing. I have used two online regex matchers (of which one was for JS-regex specifically) to test my regex against the complete source code. Both times, it had a match and returned "Olympa" exactly as it should have. However, when I have Chrome include the script on the actual page, it gives the following error:
Error in event handler for 'undefined': Cannot read property '1' of null TypeError: Cannot read property '1' of null
Obviously, the first part of my line returns "null" because it does not find a match, and taking [1] of "null" doesn't work.
I figured I might not be doing the match on the source code, but when I let the script output document.documentElement.innerHTML to the console, it outputs the complete source code.
I see no reason why this regex fails, so I must be overlooking something very silly. Does anyone else see the problem?
All help appreciated,
Kenneth
You're putting your regular expression inside a string. It should not be inside a string.
var world = document.documentElement.innerHTML.match(/boardid=[0-9]+">([A-Z][a-z]+)( - Trade){0,1}<\/a>/i)[1];
Another thing — it appears you have a document object, in which case all this HTML is already parsed for you, and you can take advantage of that instead of reinventing a fragile wheel.
var element = document.querySelector('a[href*="boardid="]');
var world = element.textContent;
(This assumes that you don't need <=IE8 support. If you do, there remains a better way, though.)
(P.S. ? is shorthand for {0,1}.)

Asserting an element is focused

According to the How do I assert an element is focused? thread, you can check if an element is focused by switching to an activeElement() and assert this is the same element you've expected to have the focus:
expect(page.element.getAttribute('id')).toEqual(browser.driver.switchTo().activeElement().getAttribute('id'));
In my case, the currently focused element does not have an id attribute.
What should I do instead of checking an id?
Bonus question: Also, as you can see from my tries to solve it, it looks like I cannot expect/assert an element (or web element) as a complete object. Why?
I've tried:
expect(page.element).toEqual(browser.driver.switchTo().activeElement());
But is is failing with an error I cannot even understand - there is a huge traceback (it is about 10 minutes to scroll in the console), but no user-friendly error inside.
I've also tried to use getWebElement():
expect(page.element.getWebElement()).toEqual(browser.driver.switchTo().activeElement());
But this resulted into the following error:
Error: expect called with WebElement argument, expected a Promise. Did
you mean to use .getText()?
Using the latest protractor development version.
In my answer I'm going to assume activeElem and pageElem are both protractor element finders, and are pointing to the same web element.
First to answer your question about why
expect(activeElem).toEqual(pageElem);
Gets into an infinite loop, it's because protractor patched jasmine's expect to resolve the promise before asserting, so that things like expect(activeElem.getText()).toEqual('text'); works without having to do
activeElem.getText().then(function(text) {
expect(text).toEqual('text');
})
You could say, why not just resolve the promise once? But then there are nested promises.
So now you might be thinking this is an issue, but it really isn't because you would never compare two elementFinders in a real use case. Jasmine's toEqual does a reference check, and not a deep compare, so expect(activeElem).toEqual(pageElem), is just the same as a simple reference comparison: (activeElem === pageElem).toToTruthy(), and there's really no point doing that. (Note element(by.css('html')) === element(by.css('html')) is false because it's not the same reference.)
So, to answer the real question for this thread: how to see if two elementFinders have the same underlying webelements:
expect(activeElem.getId()).toEqual(pageElem.getId());
It's weird that it's expecting a promise only and could not handle a webdriver element... I had the same HUGE stacktrace as you.
Anyway, would you accept this kind of solution: send a "dumb" promise with a nice comment to justify why you had to do that. It's more a workaround than a semantic solution I admit.
expect(page.element.getInnerHtml())
.toEqual(browser.driver.switchTo().activeElement().getInnerHtml());
It's working for me ;)
EDIT: Bonus answer
The reason you can't call expect with a WebElement comes from the Webdriver Control Flow Principle (I'm sure you already know about) and this line in jasminewd, the adapter for jasmine to Webdriver developped and used by Protractor ;)
For the future readers, I've created a custom matcher to assert if an element is active/focused:
beforeEach(function() {
jasmine.addMatchers({
toBeActive: function() {
return {
compare: function(elm) {
return {
pass: elm.getId().then(function (activeElementID) {
return browser.driver.switchTo().activeElement().getId().then(function (currentElementID) {
return jasmine.matchersUtil.equals(currentElementID, activeElementID);
});
})
};
}
};
}
});
});
Usage:
expect(element(by.css("#myid")).toBeActive();
What about using CSS selectors and :focus
expect(element.all(by.css('#myid:focus')).count()).toBe(1);
I solved it by using protractor's:
ElementFinder.prototype.equals
const activeElement = await browser.driver.switchTo().activeElement();
const potentialyFocusedElement = await component.$('your-locator');
const isFocused = await potentialyFocusedElement.equals(activeElement);
I am not sure how exactly equals work, if it does a deep compare, or it somehow compares instances. But it works.
Note that you can not call activeElement.equals(...) since it is not an ElementFinder, it is a WebElement.
You need to come up with a way to tell Protractor/webdriver how to find your element on the page. Protractor uses JavaScript to find elements, so any of the tools available for inspecting the DOM are available. Protractor and webdriver wrap these APIs in the various by flavors:
http://angular.github.io/protractor/#/api?view=ProtractorBy
In addition to the basic flavors, Protractor adds Angular-aware flavors (so you can search by Angular binding, etc).
If your element does not have any distinguishing characteristics, then it might be worth adding something to make it easier to test. Or, (though this is often frowned upon because its so fragile), you can use an xpath. See https://github.com/angular/protractor/blob/master/docs/locators.md
You can simply check the text (or anything else) of the element:
var currentElement = browser.switchTo().activeElement();
expect(currentElement.getText()).toEqual(page.element.getText());

null exception in javascript

I met with the following error from the following javascript functions, any ideas what is wrong?
BTW: since the whole page is long, I can not post them all here. I am trying to find a small but complete sample to reproduce this issue. Any ideas to debug further to find the root cause?
'Null' is null or not an object
Script:
<script>
$(document).ready(function() {
$("#tag0").tooltip({ effect: 'slide'});
});
</script>
thanks in advance,
George
My first guess was that
$("#tag0")
is returning null, and attempting to call a method on null is probably giving you the error. I have been informed that jQuery won't actually return null if your selector doesn't match anything -- you just get an empty set of results with a length property of 0. If you call a nonexistent method on an object of this result type, perhaps you get the error message you're seeing.
Is it possible there isn't actually an element on the page with ID "tag0"? Should it be a class instead (".tag0" instead of "#tag0")?

Categories

Resources