Playwright; asserting nested `img src` values - html

Note, this is related to my previous question here: https://stackoverflow.com/a/73043433/4190664
I am looking to further assert somethings within the DOM when I click the 'Print' button.
From troubleshooting I am seeing the following:
the pdfjs page has a #printContainer that is an empty div
when you click the Print button, it begins creating divs with the class .printedPage to represent each page of the document
within each .printedPage div is an img element with src="blob:https://mozilla.github.io/**"
Example when the print dialog is open:
<div id="printContainer">
<div class="printedPage"><img src="blob:https://mozilla.github.io/5afcff4c-aa36-4118-b4b8-011cdce6a9bc"></div>
<div class="printedPage"><img src="blob:https://mozilla.github.io/30cd3036-2d81-4b82-af9a-0f2e9c834b69"></div>
<div class="printedPage"><img src="blob:https://mozilla.github.io/047e8762-3fae-44d1-a5a0-56ea576de93e"></div>
</div>
I already am testing the following:
let requestCount = 0;
page.on('request', request => {
if(request.url().includes('blob:https://mozilla.github.io/pdf.js/web/viewer.html')) {
expect(page.locator(`.printedPage img >> nth=${requestCount}`)).toHaveAttribute('src', /blob:https:\/\/mozilla.github.io/);
requestCount++;
}
});
await printBtn.click();
await expect.poll(() => requestCount).toBe(3);
What would be the best way to assert that each .printedPage > 'img' src contains the blob information as well?
Playwright (and javascript in general) is not a strong language so I am definitely struggling on this one 😬
Any syntactical help is appreciated

You can do something like this. You can add this before the value of requestCount increments.
await expect(
page.locator(`.printedPage img >> nth=${requestCount}`)
).toHaveAttribute('src', /blob:https:\/\/mozilla.github.io/)

Related

I want to pass an HTMLDivElement as a React child. Is that impossible?

Context
I am building a React app (rails-react) where I have a parent component GameTracker that has some child components; namely, EquipmentPanel and PinnedPanels. I have added a pinned-panels-container div in the parent component where I want to move panels from the EquipmentPanel when I click on a 'pin' button.
<div id='container'>
<EquipmentPanel pinPanel={this.pinPanel}/>
<div id='tracker-contents'>
{this.state.pinnedPanels.length > 0 &&
<div id='pinned-panels-container'>
<h2>Pinned Panels</h2>
{this.state.pinnedPanels}
</div>}
</div>
</div>
Approach
The way I plan to do this is create a pinPanel() function in the parent component GameTracker, and pass it as a prop to its child, EquipmentPanel. The child then adds the button, and calls pinPanel(div), with div being the specific div/panel I want to pin.
pinPanel(panel) {
let newPanel = panel.current.cloneNode(true)
var parser = new DOMParser();
var htmlDoc = parser.parseFromString(newPanel.innerHTML, 'text/html');
newPanel = htmlDoc
let newPinnedPanels = this.state.pinnedPanels
newPinnedPanels.push(newPanel)
this.setState({
pinnedPanels: newPinnedPanels
})
}
Error
Now, whenever I pin a panel, React gives me:
Uncaught Error: Objects are not valid as a React child (found: [object HTMLDocument]).
If you meant to render a collection of children, use an array instead.
If I try to use an array, as the error message recommends, I get the same error. If I don't use DOMParser(), (which I found here), I get the same error, with the following difference: found: [object HTMLDivElement].
My question is, is there any way in React to clone a div with all its contents, pass it to another component through state, and render it in another component that is not its parent or child? I basically want to copy/paste a div.
Edit: If I try to assign it by .innerHTML, the end result is a panel with [object HTMLDocument] as a string inside.
Unsure if this is considered a proper answer, but I made it work with DOM manipulation. Feels hacky, but it works. If someone has any insight, it is of course welcome.
let newPanel = panel.current.cloneNode(true)
if (this.pinnedPanelsRef.current.innerHTML.includes(newPanel.children[0].innerHTML)) {
this.pinnedPanelsRef.current.innerHTML = this.pinnedPanelsRef.current.innerHTML.replace(newPanel.children[0].innerHTML, "")
}
else {
this.pinnedPanelsRef.current.innerHTML += newPanel.children[0].innerHTML
}
I still feel like there is a much more elegant solution that I am failing to reach.

.innerText of an element is not showing up

I have a div that is contenteditable and grabbing the div using useRef(), which is a reactjs hook.
When I try to display the text inside the contenteditable div, the alert shows nothing but the log shows the text.
Is there something I am missing?
this is just a snippet I created
export default function Input() {
const inputRef = useRef();
const showText = () => {
console.log("text: ", inputRef.current.innerText);
alert("text: ", inputRef.current.innerText);
}
return (
<>
<div ref={inputRef} contentEditable="true" supressContentEditableWarning={true} />
<button onClick={showText}>Show text</button>
</>
)
}
It also does't work when I use it as a value inside an object eg.
const obj = {
text: inputRef.current.innerText
}
I will be thankful if someone can help me understand what is going on here!!
UPDATE
just don't use alert to debug lol.
Is there anything stopping you from getting the innerText using DOM like this-
var innerText = document.getElementById('elementName').innerText
then passing the value to your reactJS?
window.alert only takes a single parameter, so only the first string is shown. If you pass in too many arguments to a javascript function, the extra parameters will simply be ignored. This is different from console.log, which is a variadic function, meaning it will take any number of parameters and display all of them.
Try alert("text: " + inputRef.current.innerText) instead.

Why does await page.Evaluate() return 'undefined'?

I'm trying to write a deploybot with nodejs, but when trying to navigate to the environments page it fails to find this button.
Here is the snippet of code:
//wait until element with unique id containing the environments button is there
await page.waitForSelector('#formatstring_widget_formatstring_14');
//check if the element actually exists, so that i can log that.
const envElement = await page.$('#formatstring_widget_formatstring_14');
if (envElement != null) {
console.log('env element exists');
} else {
console.log('no env element found');
}
const link = await page.evaluate((env)=> {
env.innerHTML;
}, envElement);
console.log('env= '+link);
If I run this, I get a log of:
'Env element exists'
'Env = undefined'
which means the element exists, but there is no innerHTML? but when I inspect the source code from the page I'm accessing, the
id=#formatstring_widget_formatstring_14 does have inner html
How is this possible?
Here is the source code
<div data-mendix-id="51_37_138" class="mx-name-formatString1 mx-link submenu-item page-nav-9" tabindex="0" id="formatstring_widget_formatstring_14" focusindex="0" widgetid="formatstring_widget_formatstring_14" style="">
<div class="formatstring ">
<a href="https://cloud.home.mendix.com/link/deploy/d22310d5-a10f-437b-93d7-c0ceab21d0c6" class="">
Environments</a>
</div></div>
It might be far easier to use the Deploy API from Mendix to automate deployment. See the API here: https://docs.mendix.com/apidocs-mxsdk/apidocs/deploy-api
Regards,
Ronald

Navigate within webpage in WebView control

I'm trying to navigate within a webpage that has been loaded from a remote server in my WebView control (Cocoa application). I would like to navigate to a particular tag that i can see in the HTML code of that page. The purpose of this all is to show the part of the HTML page that is of my interest at the top of the WebView control.
I know that in HTML code you can navigate by using something like #MIDDLE, #TOP etc. However, is this possible to do from outside of the HTML code using the WebView API?
Thanks for your reply in advance!
I found the answer to my question with the help of an other question (How to scroll HTML page to given anchor using jQuery or Javascript?).
The piece of code below does the trick for me. It searches for HTML elements with attribute: class = "container" in the HTML data that is loaded in the WebView component self.webView.
-(void) scrollMyImportantHTMLPartInView
{
// Get a list of HTML elements that contain attribute class = "container" (eg. <div class "container">)
DOMNodeList *nodeList = [[[self.webView mainFrame] DOMDocument] getElementsByClassName: #"container"];
if( nodeList && nodeList.length >= 1 ) {
// get the first node (class = "container") from the list
DOMNode *domNode = [nodeList item:0];
// Make sure it's a DOM element type
if( domNode.nodeType == DOM_ELEMENT_NODE ) {
// It's now save to cast from DOMNode* to DOMElement*
DOMElement* domElement = (DOMElement*) domNode;
// Scroll begining of HTML node into view
[domElement scrollIntoView: YES];
}
}
}

jqPlot charts on page load

I have a form where I select the number of items. Upon clicking submit, it should take me to a new page where it would display the item selected and depending on the number of items selected, it would create those many jqPlots, one for each item.
Any suggestions on how do I go about doing this?
Thanks,
S.
It's hard to give any specifics without more detail about the items, but basically you would pass a JSON structure to your view with the items to be plotted. Then you would loop through the JSON structure, creating DIV tag for each item to be plotted and appending the DIV tags to the body.
The Javascript part would look something like this:
$.each(items, function(index, value) {
$myPlot = $("<div>");
$myPlot.attr("id", "item"+index);
$.jqplot($myPlot.attr("id"), ...);
$("body").append($myPlot);
});
This question is very general, but answering (specifically and only) the question of loading multiple charts:
You need a unique HTML div id for each chart; consider using an RFC 4122 UUID (generate as needed) for each chart/div rather than a sequential index for each. Use something that looks like this as a placeholder div for each:
<div class="chartdiv" id="chartdiv-${UID}">
<a rel="api" type="application/json" href="${JSON_URL}" style="display:none">Data</a>
</div>
This embeds the JSON URL for each div inside it, in a hidden hyperlink that can be discovered by JavaScript iterating over your multi-chart HTML page.
The matter of the UUID is inconsequential -- it just seems the most robust way to guarantee a unique HTML id addressable by JavaScript for each chart.
Subsequently, you should have JavaScript that looks something like:
jq('document').ready(function(){
jq('.chartdiv').each(function(index) {
var div = jq(this);
var json_url = jq('a[type="application/json"]', div).attr('href');
var divid = div.attr('id');
jq.ajax({
url: json_url,
success: function(responseText) { /*callback*/
// TODO: responseText is JSON, use it, normalize it, whatever!
var chartdata = responseText;
jq.jqplot(divid, chartdata.seriesdata, chartdata.options);
}
});
});
});