I tend to write my own user scripts (aka Violentmonkey / Tampermonkey scripts; formerly Greasemonkey scripts). I often end up selecting elements by class name while doing so - either using native javascript or having the script load jQuery and using that.
I've noticed that sometimes I see dynamically generated class names like "SeriesIndexFooter-footer-3WmRg" with the bolded bits appearing to be some randomly generated part (I think this gets generated by React? I haven't used React myself but have sometimes seen "React" in other element names when encountering these). Obviously, I can just hard-code these classnames in my script AS-IS and it will work... but my concern is that if a site / local server app gets updated later that this will break my user script.
e.g.
// #run-at document-end
var footer = document.querySelectorAll('.SeriesIndexFooter-footer-3WmRg');
//OR
// #run-at document-end
// #require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
var footer = jQuery('.SeriesIndexFooter-footer-3WmRg');
Is there a better solution that doesn't rely on hard-coding or avoiding class names entirely but also reduces the risk that my script will break because the random portion of the class name changes?
There is no way you can predict class suffix that you are talking about.
That is used for purpose of encapsulating styles so that it applies only for that specific element or group of elements.
What you can do in your case is to use few CSS selectors that relay on searching values in attributes.
In your case it would look something like this:
document.querySelectorAll('[class*=SeriesIndexFooter-footer]')
If you need to be more specific you can chain selectors. For example you can target a type of element div. There is no limit in chaining selectors to be more precise in selecting what you need.
document.querySelectorAll('div[class*=SeriesIndexFooter-footer]')
With this line you will select all elements where class attribute has a sub-string "SeriesIndexFooter-footer" and is div.
Here is W3Schools doc for that
You can find more selectors that suite your cases on W3Schools doc here
Other word searching CSS selectors that you can use are:
[attribute=value] [target=_blank] Selects all elements with target="_blank"
[attribute~=value] [title~=flower] Selects all elements with a title attribute containing the word "flower"
[attribute|=value] [lang|=en] Selects all elements with a lang attribute value equal to "en" or starting with "en-"
[attribute^=value] a[href^="https"] Selects every <a> element whose href attribute value begins with "https"
[attribute$=value] a[href$=".pdf"] Selects every <a> element whose href attribute value ends with ".pdf"
[attribute*=value] a[href*="w3schools"] Selects every <a> element whose href attribute value contains the substring "w3schools"
Related
I have a directive w/ a template that will work in 90% of scenarios. However there are some scenarios where I'll need additional fields be added to the template. Is there a way to extend or modify an existing html template so that adding additional fields is a possibility without rebuilding 90% of an existing template?
I know that you can compose a new directive w/ a base directive. However this only serves to add functionality and content around the base directive and doesn't allow for the ability, from what I know, to insert additional html into or around the base template in a way that allows for the additional html to be recognized in the context of the base html.
In my case I have 5 standard input fields. However in some cases I need to insert additional input fields, not before or after the base 5 but literally intersperse them between the base inputs. Using bootstrap, I can apply an order to the various input fields and theoretically i can take the the additional inputs into account and supply orders accordingly. However, bootstrap cannot recognize the inputs as being part of the same row-cols ordering logic if the base content is separated into a containing div separating its content from that of any directive which composes off of it.
It seems like angularjs's directives are specifically designed to group base directive content into a single and separate container object. This is the error if you try and use the directives replace functionality w/o having a single template container: https://code.angularjs.org/1.4.7/docs/error/$compile/tplrt
Is this simply a short coming of angularjs's abilities for composition or is there some other piece to this puzzle that I'm missing?
Can't you simply do this on the front end with ... So that the input fields in question would only show up if your edge case is true. Save your condition variable in scope and based on that display/hide the fields. Unless I'm not understanding the question.
I was wonder how can I reuse an elements while I'm using appium.
An elementSerarch returns an ID which is generated by Appium , I was wonder if there a way to reuse the same element by it's id ?
If not , then what is the purpose of the element's id ?
Thanks
You can create Page Object Model (POM) is the best way for element re-usability, but these reused elements do not essentially have the same Id on different pages so you might get.
NoSuchElement Exception.
by separating out the reusable components will make your work more manageable.
Using POM You can store the elements into the variables, and pass them wherever you want.
WebElement element = driver.findElement(By.id("xyz"));
Also Preferred selector order should be : id > name > css > xpath,
id and name are often the easiest and sure way.
xpath are often brittle, css are the way to go in conjunction of id and name !
But sometimes because of Page Refresh/Loading these elements might not be available on later use so prefered way is to create Page Class and write methods to find these elements, like
public class LoginPage extends BasePage {
public void loginButton_Click() {
WebElement element = driver.findElement(By.id("xyz")).click();
}
}
Here you can list out all the methods to find the different elements on particular page, Now you just need to call the method whenever you want to use that element.
As far as Finding element by using it's Id is concern if you are using a tool such as uiautomatorviewer you will get the Developer generated Id's so you can use the same Id as many times as you want,
also if you are talking about the Id's generated by Appium such as: info: [debug] [BOOTSTRAP] [debug] Returning result: {"value":{"ELEMENT":"1"},"status":0}
Here id : 1 is internal reference for appium to act, for future actions that we call for respective element in the test code. I preferred to use driver to find the elements,
Id Of an Element
Ideally the element.getId() method can be used to return a String, representing the ID of an element, But whenever I tried to use, It always returns 1 to me, You can also use,
WebElement element = driver.findElements(By.xpath("XOXOXOXO"));
element.getAttribute("id");
Finally You need this Id to perform Click/Swipe/long click and many other events you want for your Automation Testing, basically the process is like:
Get the Id/name/xpath of an Element (By using different methods or
Tools like uiautomatorviewer)
Find that element By Name/Id/xpath etc.
Perform necessary operation
So Id is just one way to find the element, and you can reuse it by your own way.
I will use robotframework to test an application that use the sencha extjs library,
my problem is that with this library the ids are generated dynamically every time
this add new components, therefore my robotframework script would change
constantly but this is a bad idea. somebody said me that other testing frameworks
it have plugins to this task but I cant found in robotframework.
thanks in advance.
There are many ways to access elements on the page, using the id is just one way. Assuming you're using Selenium2Library, you can use any of the following locators strategies (from the Selenium2Library documentation):
Strategy Example Description
--------------- --------------------------------------- ---------------------------------
identifier Click Element | identifier=my_element Matches by #id or #name attribute
id Click Element | id=my_element Matches by #id attribute
name Click Element | name=my_element Matches by #name attribute
xpath Click Element | xpath=//div[#id='my_element'] Matches with arbitrary XPath expression
dom Click Element | dom=document.images[56] Matches with arbitrary DOM express
link Click Element | link=My Link Matches anchor elements by their link text
partial link Click Element | partial link=y Lin Matches anchor elements by their partial link text
css Click Element | css=div.my_class Matches by CSS selector
jquery Click Element | jquery=div.my_class Matches by jQuery/sizzle selector
sizzle Click Element | sizzle=div.my_class Matches by jQuery/sizzle selector
tag Click Element | tag=div Matches by HTML tag name
default* Click Link | default=page?a=b Matches key attributes with value after first '='
Note that even though some examples (such as xpath) show the use of an id, an id may not be strictly required (except, obviously, for id= and identifier=). xpath is usually the strategy-of-last-resort, because you can reference pretty much anything in the document. For more about xpath you can start here: http://en.wikipedia.org/wiki/XPath
This is not a great solution, so hopefully someone else can find a plugin or something that is easy to implement. However, if you do not find a different way, you can give every one of your Ext.js elements an ID manually by setting the "id" property on every one of your elements to a unique ID yourself. It would not take too much work, and if you named them something actually related to what they are, it would make the ID's more human readable.
From the Sencha docs:
id : String
The unique id of this component instance.
It should not be necessary to use this configuration except for singleton objects in your application. Components created with an id may be >accessed globally using Ext.getCmp.
Instead of using assigned ids, use the itemId config, and ComponentQuery which provides selector-based searching for Sencha Components analogous to DOM querying. The Container class contains shortcut methods to query its descendant Components by selector.
Note that this id will also be used as the element id for the containing HTML element that is rendered to the page for this component. This allows you to write id-based CSS rules to style the specific instance of this component uniquely, and also to select sub-elements using this component's id as the parent.
Source: http://docs.sencha.com/extjs/4.1.3/#!/api/Ext.AbstractComponent-cfg-id
With the release of Selenium2Library 1.7, users are now able to create their own custom locators. You could use this to create a location scheme based on ext's component query. So long as the overall structure of the app doesn't change that much, that may suffice. For more information on custom locators see the Selenium2Library docs.
I've started an add-on library for S2L on github that you can check out for reference, but it isn't completely finished yet.
Also, Ext lets you place unique ids on components by simply specifying the id attribute. I would recommend applying ids to all elements which you want to interact with through your tests, thereby eliminating any need for complex behavior to automate the interface.
I have seen this bunch of code in a tutorial:
var searchTerm = this.$('#searchTerm').val().trim();
I would like to understand the utility of the this. in front of the selector, it's the first time i see that.
In a Backbone.View, this.$ gives a scoped version of jQuery. It is in fact equivalent to using this.$el.find which is in turn equivalent to using $(this.el).find.
Anyhow, the reason it is a good idea to use it is that it will only access html elements from within the view's element/rendered template. Thus, you don't have to worry about the rest of the html page and you will always select the element you expect to.
Imagine that you have a view that spawns sub-views and that each of these have an editable field. If you don't use the scoped version of jQuery to get the right editable field, you will have to give a unique id to each of these html elements to make sure you will select the right one when retrieving it's content. On the other hand, if you use the scoped version, you will just have to give this editable field a class attribute and selecting this class will give you a unique element, the right one.
This is the same query as this.$el.find('#searchTerm').val().trim();
You haven't given any context to that code, but assuming it's a method inside a View, this refers to the View object.
this.$ is a shortcut to access jQuery from the View object, and is equivalent to the method this.$el.find.
I'm trying to create a custom rule (using the Rules module) so that every time the cron runs, this rule checks a date field in a custom content type I created. If that date has passed then I want to set a list widget from active to ended.
This is how far I get when trying to create this rule:
Set React on event to Cron maintenance tasks are performed
Add Condition > Set Select the condition to add to Data comparison > Continue
Here is the issue: Data selectors only has site and no access to field data.
Any ideas where I'm going wrong here?
The problem with Rules condition "Cron maintenance tasks are performed" is that at that point, there is no access to the node object so any checks/manipulations on the node are not possible. As a solution, instead of Event = Cron maintenance tasks are performed, use Event = Node: Content is viewed. You can leave it open for any content type so that when someone visits the website and opens at least one page, some action will be triggered.
You need to create a rules component first:
Go to Rules > Components (admin/config/workflow/rules/components)
Then create a new component and select 'Rule' from select list
Set a name for this component and in the table below select:
Data type: Text Token
Label: A name that you want
Machine name: Use the same name of the label but set here only lower case and underscore
Usage: Parameter
In the component add the condition 'Data comparison' and select node:type
Add other conditions that you want
Set the actions that you want and save
Now go to Rules (admin/config/workflow/rules) and create the rule with action on cron maintenance (as you have already done)
Jump the conditions section and in the actions:
New action: Add a variable
Value: Text
Then write the value of this variable just like the machine name of content type that you want to cycle on (if you want you can change the name and machine name of this variable in the section below)
Now add another action 'Fetch entity by property'
Entity type value: Node
Property value: Type
Data selector: the variable created at the point 8
Now add a loop in parameter list use the variable provided by 'Fetch entity by property'
Add an action in the loop (click on the link to the right of loop row) and select the component created in the point 2 and pass the variable provided by loop
I state: I do not know if it works but at least it should direct you towards the right path
Sorry for my english, I hope you understand everything :)
Yes you should be able to get this to work using the Rules module to implement what you're looking for, but I recommend you to also combine that with the Views Rules module. Some details about this module (from its project page):
Provides Views directly as Rules actions and loops to seamlessly use view result data.
The previous quote may seem a bit cryptic (it may make you think like "so what, how can this help me?"). Therefor some more details about how to move forward using these modules:
Create a view (using Views) so that you have 1 Views result (row) with all the nodes you want to be processed (related to your custom content type and if possible filter somehow using your date field). Whereas that view has fields (columns) for whatever is needed in subsequent steps, e.g the node ID, the date field, and possibly other fields as well. You'll need these View fields later on as values to be processed by your rule, "to set a list widget from active to ended*" (as in your question). Important: use a Views display type of "Rules".
Remove that "add condition" (in the custom rule you started) and, instead, use the Views Rules module to iterate over each of these Views results in a Rules action, using the Rules technique known as a "Rules Loop".
For each iteration step in your Rules loop, perform a Rules Action to "do your thing" (= to set a list widget from active to ended). At that point you'll have all data from each column of your Views results available as so called Rules Parameters. So at that point it's a piece of cake to adapt the value of that list widget for the node you're processing in that loop.
Optionally, you may also want to add whatever extra Rules Condition(s), also up to your own imagination. Typically the things you cannot, or have not yet, expressed as a Views filter. However, if you have a choice between using a Views filter and an extra Rules Condition, I recommend to go for the Views filter, because that will reduce the number of your iterations in your Rules loop (performance!).
Easy, no?