I have a strage website which the side menu uses href without any ID or class.
At robot framework browser library, I used click but web locator isn't detected.
I tried to use robotcorp inspector record elements, the same 'Click' method is returned as my script.
How do I locate the href or how do I re-construct the xpath?
script:
*** Settings ***
Library Browser
Resource ../Resources/BrowserParameters.robot
Resource ../Resources/BrowserResources.robot
001-Basic-Search
Click //a[#href="/test"]
report:
14:22:33.151 FAIL TimeoutError: locator.click: Timeout 10000ms exceeded.
=========================== logs ===========================
waiting for selector "//a[#href="/test"]"
selector resolved to hidden
attempting click action
waiting for element to be visible, enabled and stable
element is not visible - waiting...
============================================================
The web element:
<span>test</span>
full HTML
<li class="ant-menu-item ant-menu-item-selected" role="menuitem" style="padding-left: 24px;">
<span role="img" aria-label="user-switch" class="anticon anticon-user-switch ant-menu-item-icon">
<svg viewBox="64 64 896 896" focusable="false" class="" data-icon="user-switch" width="1em" height="1em" fill="currentColor" aria-hidden="true">
<defs>
<style/>
</defs>
<path d="M759 335c0-137-111-248-248-248S263 198 263 335c0 82.8 40.6 156.2 103 201.2-.4.2-.7.3-.9.4-44.7 18.9-84.8 46-119.3 80.6a373.42 373.42 0 00-80.4 119.5A373.6 373.6 0 00136 874.8a8 8 0 008 8.2h59.9c4.3 0 7.9-3.5 8-7.8 2-77.2 32.9-149.5 87.6-204.3C356 614.2 431 583 511 583c137 0 248-111 248-248zM511 507c-95 0-172-77-172-172s77-172 172-172 172 77 172 172-77 172-172 172zm105 221h264c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H703.5l47.2-60.1a8.1 8.1 0 001.7-4.9c0-4.4-3.6-8-8-8h-72.6c-4.9 0-9.5 2.3-12.6 6.1l-68.5 87.1c-4.4 5.6-6.8 12.6-6.8 19.8.1 17.7 14.4 32 32.1 32zm240 64H592c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h176.5l-47.2 60.1a8.1 8.1 0 00-1.7 4.9c0 4.4 3.6 8 8 8h72.6c4.9 0 9.5-2.3 12.6-6.1l68.5-87.1c4.4-5.6 6.8-12.6 6.8-19.8-.1-17.7-14.4-32-32.1-32z"/>
</svg>
</span>
<span>
<a href="/test"/>test</span>
</li>
I tried this does not work as well:
Click xpath://a[#href='/test']
Error: locator.click: Unsupported token "#href" while parsing selector "xpath://a[#href='/test']"
The answer is really simple:
Click "test"
Related
What's the best way to integrate Tailwindcss Heroicions into Laravel 9?
This is my workflow. I created a "symbols" folder in my Laravel file structure:
resources > views > components > symbols
Then I download and save each SVG file: arrow-down.blade.php.
In the SVG blade file I empty the class attribute and replace it with {{ $class }}:
<svg xmlns="http://www.w3.org/2000/svg" class="{{ $class }}">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z" />
</svg>
And integrate it like this:
<x-symbols.star class="w-6 h-6"/>
Is there a better way? Can I download Heroicons as Blade files so I don't have to do this manual work?
I am making a pair of custom web components to show notifications. The wc-notifier is the parent, which creates wc-notifications on itself, there maybe multiple notifications shown at the same time, in which case one parent, many children.
Each web component has links to two external stylesheets:
base.mins.css
It's own shadow.mins.css
As you can see below I duplicated the links because I am trying to use the preload functionality of the browser:
I know I could inline the stylesheet, but I would like to link the styles, for the same reason we normally link stylesheets.
<template id="TEMPLATE_wc-notification">
<link rel="stylesheet" type="text/css" href="/static/csslib/base.min.css">
<link rel="preload" as="style" type="text/css" href="/static/csslib/base.min.css">
<link rel="stylesheet" type="text/css" href="/static/wclib/wc-notification/shadow.min.css">
<link rel="preload" as="style" type="text/css" href="/static/wclib/wc-notification/shadow.min.css">
<div id="CONTAINER" class="d:f a-i:s o-y:a max-h:80vh"><div name="icon" class="o-align:c-m h:64"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="INFO"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="SUCCESS"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="WARNING"><path d="M0 0h24v24H0z" fill="none"/><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="ERROR"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg></div><slot id="SLOT" class="d:b f-s:24 f-w:500 o-w:a m-t:10"></slot><div id="CLOSE" class="o-align:c-m a-r:1/1 c:p bg-c:black|15a#h b-r-r:6 h:64"><svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 0 24 24" width="32px" fill="black" draggable="false" class="p:4"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></div></div>
</template><script type="module" async src="/static/wclib/wc-notification/component.min.js"></script>
<template id="TEMPLATE_wc-notifier">
<link rel="stylesheet" type="text/css" href="/static/csslib/base.min.css">
<link rel="preload" as="style" type="text/css" href="/static/csslib/base.min.css">
<link rel="stylesheet" type="text/css" href="/static/wclib/wc-notifier/shadow.min.css">
<link rel="preload" as="style" type="text/css" href="/static/wclib/wc-notifier/shadow.min.css">
<div id="CONTAINER" class="p:f r:0 t:0 d:f f-d:c a-i:e z-i:1000" hidden><slot></slot></div>
</template><script type="module" async src="/static/wclib/wc-notifier/component.min.js"></script>
Say I perform a fetch, and the server does not respond. Then I wish to show a notification that the submission failed. Now the problem comes in that I would like to show a notification about the failed fetch.
However when I try create the notification, the browser then wishes to re-download the stylesheet for the notification, however now there is "no connection", so the notification is not styled.
Heres the response Headers (showing its not set to no-cache etc):
Note I test the failed fetch within about 10 seconds of the page loading:
Cache-Control: max-age=6000
Content-Disposition: inline; filename="component.min.js"
Content-Length: 1105
Content-Type: application/javascript
Date: Wed, 21 Sep 2022 20:47:35 GMT
Last-Modified: Wed, 21 Sep 2022 20:47:35 GMT
Server: WSGIServer/0.2 CPython/3.9.13
Set-Cookie: ... expires=Sat, 22 Oct 2022 20:47:35 GMT; HttpOnly; Max-Age=2678400; Path=/; SameSite=Lax
Vary: Cookie
Why does the browser try to redownload the stylesheet? How do I make it download once at start up, and not again everytime I create an element of wc-notification? The above scenario is the worst case, but even good cases its slow to style the component, because everytime I add a web component, the stylesheet is re-downloaded.
I also tried #import, but it results in the same issue, the stylesheet is downloaded every time.
<template id="TEMPLATE_wc-notification"><style>#import "/static/csslib/base.min.css"</style>
<style>#import "/static/wclib/wc-notification/shadow.min.css"</style>
<div id="CONTAINER" class="d:f a-i:s o-y:a max-h:80vh"><div name="icon" class="o-align:c-m h:64"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="INFO"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="SUCCESS"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="WARNING"><path d="M0 0h24v24H0z" fill="none"/><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="ERROR"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg></div><slot id="SLOT" class="d:b f-s:24 f-w:500 o-w:a m-t:10"></slot><div id="CLOSE" class="o-align:c-m a-r:1/1 c:p bg-c:black|15a#h b-r-r:6 h:64"><svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 0 24 24" width="32px" fill="black" draggable="false" class="p:4"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></div></div>
</template><script type="module" async src="/static/wclib/wc-notification/component.min.js"></script>
<template id="TEMPLATE_wc-notifier"><style>#import "/static/csslib/base.min.css"</style>
<style>#import "/static/wclib/wc-notifier/shadow.min.css"</style>
<div id="CONTAINER" class="p:f r:0 t:0 d:f f-d:c a-i:e z-i:1000" hidden><slot></slot></div>
</template><script type="module" async src="/static/wclib/wc-notifier/component.min.js"></script>
I did come across Constructable Style Sheets when trying to look fo ra solution, but Safari does not support the replace and replaceSync methods, which are the heart of it for web components.
I'm assuming that:
You are including the templates in the HTML of the web page
Since one of your css files is named "shadow.min.css", that you are using a Shadow DOM.
You are wanting to make a single http call for your css files regardless of the number of custom tags in the web page.
Since you are building a web component, you should adhere to the principle of compartmentalization, meaning: A user of the component only has to include a [script] tag in the HTML and everything else gets loaded by the component - and stays in the component.
When you inject a [link] tag into the Shadow DOM from within your component, the browser will cache the file that was retrieved. Usually, that's all you have to do.
For the templates, since we don't have HTML imports at the moment, the easiest way to fetch a template ONLY ONCE in spite of the number of custom tags in the web page is to make use of native Javascript modules. Modern browsers seem to have good support for native JS modules. CAVEAT, your template file must have a .js extension or it won't work ie. template.js. If this doesn't work for your particular situation, then you have to store the content of the template somehow - see the answer to this question: Single HTTP request for HTML template file for multiple instances of the same Web Component
So, assuming a directory structure like this:
Document-root
- index.html
-/static
-- /components
--- /wc-notification
---- component.js
---- template.js
--/csslib
--- base.min.css
--- /wclib
---- /wc-notification
----- shadow.min.css
index.html
<head>
<script type="module" src="/static/components/wc-notification/component.js"></script>
</head>
<body>
<wc-notification>Foo</wc-notification>
<wc-notification>Bar</wc-notification>
<wc-notification>Baz</wc-notification>
</body>
component.js
import { template } from "./template.js";
export class WCNotificaton extends HTMLElement {
#css_file1 = "/static/csslib/base.min.css";
#css_file2 = "/static/wclib/wc-notification/shadow.min.css"
constructor() {
super();
const style = document.createElement('style');
const fragment = document.createRange().createContextualFragment(template);
style.setAttribute("rel", "stylesheet");
this.style1 = style.cloneNode();
this.style1.setAttribute("href", this.css_file1);
this.style2 = style.cloneNode();
this.style2.setAttribute("href", this.css_file2);
this.notification_template = fragment.querySelector('#TEMPLATE_wc-notification').content;
this.notifier_template = fragment.querySelector('#TEMPLATE_wc-notifier').content;
this.shadow = this.attachShadow({ mode: "open" })
}
connectedCallback() {
this.doSomethingwithTheTemplates();
this.render();
}
doSomethingwithTheTemplates() {
this.notify = // the result of processing the templates, should be a DOM Node;
}
render() {
this.shadow.append(this.style1);
this.shadow.append(this.style2);
this.shadow.append(this.notify);
}
document.addEventListener('DOMContentLoaded', customElements.define('wc-notification', WCNotificaton);
template.js (I'm using a template literal here because it's easy)
export const template = `
<template id="TEMPLATE_wc-notification">
<!-- Do not include <link> or #import, <style> is OK -->
</template>
<template id="TEMPLATE_wc-notifier">
<!-- Do not include <link> or #import, <style> is OK -->
</template>
`;
I wanted to navigate to next page but the robot always returned error
How do I construct the xpath with this <li title="2" class="ant-pagination-item ant-pagination-item-2" tabindex="0">
or <li title="Next Page" class="ant-pagination-next"
script:
*** Settings ***
Library Browser
Library OperatingSystem
Resource ../Resources/BrowserParameters.robot
Resource ../Resources/BrowserResources.robot
Resource ../Resources/BrowserCustomKeywords.robot
#Select Browser: chromium or firefox
Test Setup Test Setup Browser=chromium
Test Teardown Test Teardown
*** Test Cases ***
001-01-Upload13Videos-Delete2Videos-Upload2Videos-Process
Click ${Page-Inference}
Upload Multiple Video Files
Click //title[#class="ant-pagination-item ant-pagination-item-2"])
the outer HTML
<ul class="ant-pagination mini ant-table-pagination ant-table-pagination-right" unselectable="unselectable">
<li title="Previous Page" class="ant-pagination-prev ant-pagination-disabled" aria-disabled="true">
<button class="ant-pagination-item-link" type="button" tabindex="-1" disabled="">
<span role="img" aria-label="left" class="anticon anticon-left">
<svg viewBox="64 64 896 896" focusable="false" data-icon="left" width="1em" height="1em" fill="currentColor" aria-hidden="true">
<path d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"/>
</svg>
</span>
</button>
</li>
<li title="1" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active" tabindex="0">
<a rel="nofollow">1</a>
</li>
<li title="2" class="ant-pagination-item ant-pagination-item-2" tabindex="0">
<a rel="nofollow">2</a>
</li>
<li title="Next Page" class="ant-pagination-next" aria-disabled="false" tabindex="0">
<button class="ant-pagination-item-link" type="button" tabindex="-1">
<span role="img" aria-label="right" class="anticon anticon-right">
<svg viewBox="64 64 896 896" focusable="false" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true">
<path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"/>
</svg>
</span>
</button>
</li>
</ul>
error:
Error: locator.click: DOMException: Failed to execute 'evaluate' on 'Document': The string './/title[#class="ant-pagination-item ant-pagination-item-2"])' is not a valid XPath expression.
at Object.queryAll (<anonymous>:3:42890)
at g._queryEngineAll (<anonymous>:3:49586)
at g._querySelectorRecursively (<anonymous>:3:48960)
at g.querySelectorAll (<anonymous>:3:49365)
at eval (eval at evaluate (:3:2389), <anonymous>:10:33)
at s (<anonymous>:3:51298)
at <anonymous>:3:51366
at Object.run (<anonymous>:3:51732)
at eval (eval at evaluate (:3:2389), <anonymous>:1:14)
at t.default.evaluate (<anonymous>:3:2412)
=========================== logs ===========================
waiting for selector "//title[#class="ant-pagination-item ant-pagination-item-2"]) >> nth=0"
Failed to execute 'evaluate' on 'Document': The string './/title[#class="ant-pagination-item ant-pagination-item-2"])' is not a valid XPath expression.
============================================================
You are using this xpath
//title[#class="ant-pagination-item ant-pagination-item-2"])
which is wrong, cause xPath expression are written like this
//tagName[#attributeName='attributeValue']
so your xpath should be
//li[#class='ant-pagination-item ant-pagination-item-2']
Also, You can have this xpath, since you are mainly looking for pagination, index wouldn't hurt [See below]
//li[#title='2']
Please take a look at my approach and tell me is it a good/correct way to add a complex DOM element inside Fullcalendar header.
viewSkeletonRender(info){
this.$refs.fullCalendar.$el.querySelector('.fc-header-toolbar').insertAdjacentHTML('afterbegin', `
<div class="event_create-wrap">
<div class="event_create-btn">
<div class="event_create-btn-left">
Create
</div>
<div class="event_create-dropdown">
<button class="event_create-btn-dropdown" #click="eventDropdownClicked">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M111 13.8822L3.62734 8.39658C3.23555 8.01742 3.23555 7.40432 3.62734 7.0292L4.5693 6.11762C4.96109 5.73846 5.59462 5.73846 5.98224 6.11762L10.0002 10.006L14.0181 6.11762C14.4099 5.73846 15.0434 5.73846 15.431 6.11762L16.373 7.0292C16.7648 7.40836 16.7648 8.02146 16.373 8.39658L10.7045 13.8822C10.3211 14.2614 9.68756 14.2614 9.29578 13.8822Z" fill="white"/>
</svg>
</button>
<div class="event_create-btn-dropdown-content" :class="{show: isEventListShown}">
1
2
3
</div>
</div>
</div>
</div>
`);
}
I am using "viewSkeletonRender" hook but i am not really sure that using "insertAdjacentHTML" is good, since i've used it first time.
I can't use AppendChild because i need to add so many html code there and it is difficult to create all html objects step by step (what if i need to add 100 html tags...) .
I can't use innerHtml because it will remove all what i have in fc-header-toolbar.
And as i understand i can't bind any VueJs props and methods to this HTML with this approach because i add this html as vanila Js.
Thank you for your advice.
I have a Blueimp gallery working, but when the page loads, it immediately goes to slideshow mode, instead of only displaying the thumbnails.
I'd like to display thumbnails first, so that if someone wants to start in the middle of the show they can easily do that.
How can I stop it from automatically going to slideshow mode?
Here is my html
18 <div id="blueimp-gallery" class="blueimp-gallery blueimp-gallery-controls" data-start-slideshow="false">
19 <!-- The container for the modal slides -->
20 <div class="slides"></div>
21 <!-- Controls for the borderless lightbox -->
22 <h3 class="title"></h3>
23 <p class="description"></p>
24 <a class="prev">‹</a>
25 <a class="next">›</a>
26 <a class="close">×</a>
27 <a class="play-pause"></a>
28 <ol class="indicator"></ol>
29 <!-- The modal dialog, which will be used to wrap the lightbox content -->
30 <div class="modal fade">
31 <div class="modal-dialog">
32 <div class="modal-content">
33 <div class="modal-header">
34 <button type="button" class="close" aria-hidden="true">×</button>
35 <h4 class="modal-title"></h4>
36 </div>
37 <div class="modal-body next"></div>
38 <div class="modal-footer">
39 <button type="button" class="btn btn-default pull-left prev">
40 <i class="glyphicon glyphicon-chevron-left"></i>
41 Previous
42 </button>
43 <button type="button" class="btn btn-primary next">
44 Next
45 <i class="glyphicon glyphicon-chevron-right"></i>
46 </button>
47 </div>
48 </div>
49 </div>
50 </div>
51 </div>
60 <div id="links">
61 #foreach ($finalQuery as $image)
62 <a href="{{ URL::route ('ppMiscGetProtectedFile', [ 'fileID' => $image['fileID'], 'size' => 'original' ] ) }}"
63 title="{{ $image['image_number'] }}"
64 data-description="Year: {{ $image['year'] }}"
65 data-gallery
66 >
67 <img src="{{ URL::route( 'ppMiscGetProtectedFile', [ 'fileID' => $image['fileID'], 'size' => 'thumbSmall'] ) }}" alt="{{ $image['image_number'] }}" />
68 </a>
69 #endforeach
70
71 </div>
And here is the Javascript
78 <script src="//blueimp.github.io/Gallery/js/jquery.blueimp-gallery.min.js"></script>
79 <script src="/js/bs_gallery/blueimp-gallery.min.js"></script>
80
86 <script>
87 blueimp.Gallery(
88 document.getElementById('links').getElementsByTagName('a'),
89 {
90 startSlideshow:false,
91 onslide: function (index, slide) {
92 var text = this.list[index].getAttribute('data-description'),
93 node = this.container.find('.description');
94 node.empty();
95 if (text) {
96 node[0].appendChild(document.createTextNode(text));
97 }
98 }
99 }
100 );
101
102
103 var options = {
104 startSlideshow: false,
105 };
106
107
108
109
110 </script>
It works, I get the gallery and I get the description field showing up (the Year: tag). It took a while to figure out how to get the description field to show. The blueimp documentation code doesn't work, but searching stack exchange got that fixed.
But now it always starts in slideshow mode. How do I get it to default to displaying the wall of thumbnails instead?
The reason it starts immediately is that you are creating the gallery instance as soon as the script loads. The way to load the thumbnail view first is to put the gallery initializer code inside a click handler set up on the thumbnail images.
Example:
<script>
document.getElementById('links').onclick = function (event) {
event = event || window.event;
var target = event.target || event.srcElement,
link = target.src ? target.parentNode : target,
options = { index: link, event: event},
links = this.getElementsByTagName('a');
blueimp.Gallery(links, options);
};
</script>
Also, the startSlideshow option is for controlling whether the slideshow begins when the gallery launches, which I think is different than what you are after.