The component that I have testing renders something this:
<div>Text<span>span text</span></div>
As it turns out for testing the only reliable text that I have is the 'span text' but I want to get the 'Text' part of the <div>. Using Jest and react-testing-library I can
await screen.findByText(spanText)
This returns an HTMLElement but it seems limited as I don't have any of the context around the element. For example HTML methods like parentNode and previousSibling return null or undefined. Ideally I would like to get the text content of the parent <div>. Any idea how I can do this with either Jest or react-testing-library?
A good solution for this is the closest function.
In description of closest function is written: Returns the first (starting at element) including ancestor that matches selectors, and null otherwise.
The solution would look like this:
screen.getByText("span text").closest("div")
Admittedly, Testing Library doesn't communicate clearly how to do this. It includes an eslint rule no-direct-node-access that says "Avoid direct Node access. Prefer using the methods from Testing Library". This gives the impression that TL exposes a method for a situation like this, but at the moment it does not.
It could be you don't want to use .closest(), either because your project enforces that eslint rule, or because it is not always a reliable selector. I've found two alternative ways to tackle a situation like you describe.
within():
If your element is inside another element that is selectable by a Testing Library method (like a footer or an element with unique text), you can use within() like:
within(screen.getByRole('footer')).getByText('Text');
find() within the element with a custom function:
screen.getAllByText('Text').find(div => div.innerHTML.includes('span text'));
Doesn't look the prettiest, but you can pass any JS function you want so it's very flexible and controllable.
Ps. if you use my second option depending on your TypeScript config you may need to make an undefined check before asserting on the element with Testing Library's expect(...).toBeDefined().
But I have used HTML methods a lot and there was no problem yet. What was your problem with HTML methods?
You can try this code.
const spanElement = screen.getElementByText('span text');
const parentDiv = spanElement.parentElement as HTMLElement;
within(parentDiv).getElementByText('...');
Related
I can not understand what it exactly does according to the documentation.
This documentation is not detailed enough.
Is there an example?
The documentation isn't that deep because what this does isn't that big of a deal, all that this decorator does is create a "shortcut" of sorts for calling querySelector on the element's root (be it this.shadowRoot if you're using shadow dom or this if you're not)
So basically this TS code:
#query('.someClass')
private _someClassElement: Element
is the same as doing this in JS
get _someClassElement() {
return this.shadowRoot.querySelector('.someClass');
// or this.querySelector('.someClass') if you're not using shadow dom
}
I'm using Jquery to get a list of elements having a class "x".
html:
<p class="x">Some content</p>
<p class="x">Some content#2</p>
If we use Jquery to get both these html elements and do something with it- we use something like:
$(".x").text("changed text");
This will change the text of both the paragraphs. From $(".x") - How can we add a array - subscript notation like we can do with getElementsByclassName as follows:
document.getElementsByClassName("x")[0].innerHTML
I tried this
$(".x")[0].text("asasa")- it doesn't work gives a typeerror in javascript console. I also tried get API here -http://jsfiddle.net/probosckie/jnz825mp/ - and it doesnt work
the error is Uncaught TypeError: $(...).get(...).text is not a function
None of the solutions below WORK!
You can use the get() method for accessing an element from the array, for example:
$(".x").get(index).textContent = "changed text";
More info: https://api.jquery.com/jquery.get/
And for obtaining HTML (innerHTML) you call the .html() function:
// This is equal to document.getElementsByClassName("x")[0].innerHTML
$(".x").get(0).innerHTML;
If you want to set the HTML, then just provide your HTML code inside the function call like this .html('<h1>Hello, World!</h1>').
EDIT: .get() returns the DOM object not the jQuery wrapped element. Therefore .text() and .html() doesn't work. Unless you wrap it.
More options:
$(".x").get(0).innerHTML;
$($(".x").get(0)).html();
$(".x:first").html();
You can do it like this way:
$('.x:eq(0)').text('changed text');
or:
$('.x').eq(1).text('bbb');
both works well
sorry for my before answer..
The solution $(".x").get(index)... will first match all .x (which is bad performance). And then it will filter
If you have 1000 .x it will fill an 1000 items in the jQuery object (before filtered)
But
$(".x:first").text("changed text"); will do better because it won't yield all .x and then filter , but will do it at a first single step (without filling 1000 items)
So, I'm trying to access the input field hidden deep within a paper input field. This is so that I can change the input type and so on. After inspecting the element, You can see that it has 2 shadow roots as explained in this blog. However, the method explained in that blog no longer works. I'm using dart version 1.5.3, polymer 0.12.0-dev.
I try to query the paper input like so:
querySelector('#paper-input-id').shadowRoot.querySelector('#input');
However, that returns null. This is because the shadowRoot property only returns the first shadow root. The input field is buried in the second shadow root. I guess what I am asking is if there is a generic way to select the nth-shadow root of an element?
This seems exactly how I did it in the unit test for <core-input>
var input = dom.document.querySelector("#changeAndInputEvent") as CoreInput;
var domInput = (input.shadowRoot.olderShadowRoot.querySelector('#input') as dom.InputElement);
what also should work is
var domInput = (dom.document.querySelector("#changeAndInputEvent /deep/ #input");
or
var domInput = (dom.document.querySelector("* /deep/ #changeAndInputEvent /deep/ #input");
when the paper-input itself is inside a shadow-dom
Instead of using shadowRoot and olderShadowRoot, which may change if for whatever reason paper-input decides to inherit something new that then inherits from core-input, try using the more generic shadowRoots map (note the 's'):
querySelector('#paper-input-id').shadowRoots['core-input'].querySelector('#input');
To set the html of elements on my site, I use mostly
$('elementId').innerHTML = "<p>text</p>";
Looking through the mootools docs, I found this example given:
$('myElement').set('html', '<div></div><p></p>');
Is there any difference between these? Should I go through and change .innerHTML to the mootools method, or doesn't it make a difference?
the reason why the first one works is because - as it stands - a $ selector (document.id) in mootools returns the actual element. this - in normal browsers - is identical to document.getElementById() and the element object exposes any and all of its attributes/properties for you to edit.
the problems with NOT using .set are:
when mootools 2.0 aka MILK gets released, it won't work as it will be wrapped like jQuery and the selector won't return the object (mootools is becoming AMD hence it won't modify native Types - Element, Array, Number, String, Function(maybe!) - prototypes).
you cannot chain this. with set you can: $('someid').set("html", "loading...").highlight();, for example.
set is overloaded - it can set either a single property or multiples by means of passing an object. eg, element.set({html: "hello", href: "#", events: boundObj});
look at https://github.com/mootools/mootools-core/blob/master/Source/Element/Element.js#L936-942 - you can pass an array as an argument and it will join it for you, this makes it easy to work with multi-line strings and ensures performance in IE
edit: the BBT fan has kind of opened a separate topic: should the framework try to block you / prevent you from doing things that break the browser?
if you want to, you can add disallowed elements by changing that setter Element.Properties.html.set = function() { var tag = this.get("tag"); ... check tag }; - isn't mootools great?
mootools - by default - will NOT try to prevent you from doing stupid shit [tm] - that's your responsibility :) try setting height on an element to a negative value in IE, for example. should the Fx class prevent you from doing that? No. Should the setter prevent you? No. The footprint of constant checks to see if you are not breaking means it will slow everything down in performance-critical cases like animations.
var parent = el.getParent();
parent.getElement('div[class=test]'); // return array
var parent1 = el.parentNode;
parent1.getElement('div[class=test]'); // error getElement is not a function
It seems parent1 doesn't have all element methods of MooTools, how to extend all element method of parent1, like in page
Note: I have to use parentNode.
parent.getElement('div[class=test]');
should really be
parent.getElement("div.test");
there's a substantial difference going to element.getParent() and element.parentNode - it boils down to Element prototype, which cannot be extended in old versions of IE.
mootools works around that by saving a reference to the methods directly on the elements instead as properties.
hence if you do element.getParent() and that returns an element, this will extend it to have all the prototypes. element.parentNode returns a simple element object, which will work in browsers where the Element.prototype is inherited correctly.
you can make the second method work in IE by doing:
var parent1 = el.parentNode;
$(parent1).getElement("div.test");
Subsequent references to parent1 do not need the $ (or document.id) as the element will already have been extended.
so to summarize the answer:
to make an element extended, you need to run it through a selector.
var parent = el.parentNode;
$(parent); // this extends it.
parent.getElements("div.test").something()
Both ways work just fine on an element, proof: http://jsfiddle.net/SuJn6/
I assume what you're doing wrong is your el is actually an Element Collection, not a single element. In which case you need to loop your first array, and only then use parentNode, example: http://jsfiddle.net/35Fxf/
Pro-tip: name your variable carefully, el and els - all makes a huge difference.