i want to automate some browser tasks using Python and Selenium webdriver on Chromium browser. My python script is already able to login, navigate to a subpage / do some clicks, and insert something into a form.
My problem is a mandatory dropdown list where i have to choose something before i can go on. I think the webpage contains angularjs / javascript at that point (third line in the code below) to create the dropdown, and i don't know how to handle that.
Problem seem to be 1) to locate the element (xpath seems to change sometimes), and 2) i'm unable to click or send keys to what i've found. Also i've tried some kinds of "WebDriverWait" and sleep commands and "wait.until(expected_conditions.visibility_of_element_located((By.XPATH, ...))"... no luck so far.
Is it at all possible to solve that problem with just Python and Selenium?
Or do i need something like Protractor (and does Protractor just works with Javascript commands)? Also i've seen Pytractor...
I'm quite a Newbie concerning that stuff, could someone please explain what could be a good way to solve this problem? Thanks in advance... :)
Webpage code looks like this (grabbed using Firebug/Firepath):
<div class="ng-scope ng-isolate-scope" model-contains-key="true" ref="salutations" cat-input-select="editDetail.salutation">
<div id="s2id_autogen1" class="select2-container form-control ng-untouched ng-valid ng-dirty ng-valid-parse">
<a class="select2-choice select2-default" tabindex="-1" href="javascript:void(0)">
<span id="select2-chosen-2" class="select2-chosen"/>
<abbr class="select2-search-choice-close"/>
<span class="select2-arrow" role="presentation">
<b role="presentation"/>
</span>
</a>
<label class="select2-offscreen" for="s2id_autogen2"/>
<input id="s2id_autogen2" class="select2-focusser select2-offscreen" type="text" role="button" aria-haspopup="true" aria-labelledby="select2-chosen-2"/>
<div class="select2-drop select2-display-none select2-with-searchbox">
<div class="select2-search">
<label class="select2-offscreen" for="s2id_autogen2_search"/>
<input id="s2id_autogen2_search" class="select2-input" type="text" aria-autocomplete="list" aria-expanded="true" role="combobox" spellcheck="false" autocapitalize="off" autocorrect="off" autocomplete="off" aria-owns="select2-results-2" placeholder=""/>
</div>
<ul id="select2-results-2" class="select2-results" role="listbox"/>
</div>
</div>
<select class="form-control ng-untouched ng-valid ng-dirty ng-valid-parse" ng-change="modelChanged(); changeCallback({value: selectValue.value})" ng-readonly="readonly" ng-disabled="disabled" ng-model="selectValue.value" ui-select2="{dropdownAutoWidth: 'true', allowClear: 'false'}" tabindex="-1" title="" style="display: none;">
<option value=""/>
<!-- ngRepeat: option in options -->
<option class="ng-binding ng-scope ng-isolate-scope" cat-i18n="xxxxxxx.salutation.Mr" value="Mr" ng-repeat="option in options">Mr</option>
<!-- end ngRepeat: option in options -->
<option class="ng-binding ng-scope ng-isolate-scope" cat-i18n="xxxxxxx.salutation.Ms" value="Ms" ng-repeat="option in options">Ms</option>
<!-- end ngRepeat: option in options -->
</select>
<!-- ngIf: infoText -->
</div>
In order to automate any webpage developed in Angular JS, we have to use an opensource tool "Protractor". Selenium itself won't be supporting the Angular JS web applications automation. For more information on Protractor, please refer to the below article
Testing Angular JS application using Protractor
The protractor also uses the same commands which selenium uses.
Hope this helps.
Related
I am trying to load a dropdown with angularjs and semantic UI with the following code
<div id="state" class="ui search selection dropdown">
<label for="state">Estado</label>
<input type="hidden" name="state">
<i class="dropdown icon"></i>
<div class="default text">Selecione o Estado</div>
<div class="menu" ng-model="state" ng-change="loadCity()">
<div ng-repeat="state in states" class="item" data-value="{{state.ID}}">{{state.NAME}}</div>
</div>
</div>
<div>
But the event is not called.
When I use the code below it works:
<select id="state" class="ui search selection dropdown" ng-model="state" ng-init="loadState()" ng-change="loadCity(state.ID)">
<option ng-repeat="state in states" value="{{state.ID}}">{{state.NAME}}</option>
</select>
What am I doing wrong?
Looks like this is an issue other people have encountered. Take a look at this issue: https://github.com/Semantic-Org/Semantic-UI/issues/1913
I found a way to solve this issue on angular side using some 1.3 magic. It's not the best way of doing this but it works.
It can simply be solved by using the formController and a watch.
I updated the plnkr. Note that i use underscore.js to find the array index of the control inside the form.
The rest is pure angular and some jquery.
http://plnkr.co/edit/eDOej3zJ0bwQ1xyX1LDI?p=preview
Look at the section starting with:
app.directive('select'
I have a dropdown which i am trying to populate from the javascript. But i am unable to do it.
This is my HTML
<div class="ui fluid search selection dropdown values">
<input type="hidden" name="values" selectOnBlur={false}>
<i class="dropdown icon"></i>
<div class="default text">Select a category</div>
<div class="menu">
</div>
</div>
And this is the JS
$('.ui.dropdown.values').dropdown({values:[{name:"oi",value:32},{name:"yo",value:2}]})
Please find the codepen link below
CodePen
Thanks
Can be fixed by changing versions to:
https://code.jquery.com/jquery-3.2.1.min.js
https://cdn.jsdelivr.net/npm/semantic-ui#2.2.13/dist/semantic.min.js
Because the dropdown library's version you used didn't support populating via JS (as per docs)
I have a bootstrap dropdown menu combined with a text input field that acts as an Angular filter. The code looks like this:
<form name="searchForm" role="form" data-ng-submit="goSearch()">
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<div class="dropdown">
<input type="text" ng-click="load_origins()" name="dropdown-toggle" data-ng-model=origin_search placeholder="Ciudad Origen" class="btn btn-default search-menu-button dropdown-toggle" id="origin" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false" required autocomplete="off">
<ul class="dropdown-menu dropdown-menu-right search-menu-items" aria-labelledby="origin">
<li><a href class= "search-menu-item" data-ng-repeat="origin in origins | filter:origin_search" data-ng-click="checkDestinations(origin)">{{origin.Localidad}}</a></li>
</ul>
</div>
</div>
</div>
</div>
</form>
The thing is that:
<input type="text" ng-click="load_origins()" name="dropdown-toggle" data-ng-model=origin_search placeholder="Ciudad Origen" class="btn btn-default search-menu-button dropdown-toggle" id="origin" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false" required autocomplete="off">
is not working as intented in iOS, the keyboard appears but no text appears when the user tries to write.
For what I've been searching it can be related with a bug in iOS with that particular input type, but there was no solution that worked for my case.
Is it maybe a CSS related thing? (Maybe a Bootstrap bug: http://getbootstrap.com/browser-bugs/)
Or could it be re an Angular thing? Or maybe just an iOS problem?
Any help will be appreciated.
Thanks in advance.
Finally I opted to use a Typeahead of UI-Bootstrap mostly because it seemed a good option as it behaved the same way that the dropdown that I tried to implement, but without the troubles on iOS.
So, I didn't find out what was causing the problems, after some frustration it was a better option to change the directive.
Background:
I administer an Amazon shop for a category that is unsupported by the Amazon bulk upload tool. The shop contains (once this question is resolved) +150 individual products with 6-9 preview images each and in 6 EU sites (huge manual upload effort).
I decided to use Selenium and my basic python skills to automate the upload.
Problem:
All works perfectly fine except for image upload. I had used this code in the past to send images into an input field:
driver.find_element_by_css_selector('input[type="file"]').send_keys('path/to/local/image')
Modifying this to fit to the image upload page in question:
driver.find_element_by_id('main_image_url').send_keys('path/to/local/image')
Returns a sea of red:
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 192, in check_response
raise exception_class(message, screen, stacktrace)
ElementNotVisibleException: Message: element not visible
(Session info: chrome=53.0.2785.116)
(Driver info: chromedriver=2.23.409710 (0c4084804897ac45b5ff65a690ec6583b97225c0),platform=Mac OS X 10.11.6 x86_64)
Also tried:
I have read around a bit but my knowledge of Javascript is null so I have blindly tried a few javascript snippets to make the element visible first to no avail.
driver.execute_script('document.getElementById("main_image_url").style.visibility = "visible";')
I've also read some solutions involving trying to navigate the actual dialogue window with complicated combinations of send_keys() but sound like trouble.
I'm on a Mac and using the Chrome driver.
Any tips much appreciated
HTML element of page:
<div id="main_image_url-wrapper" class="a-row a-spacing-medium input-container">
<div data-attribute-name="main_image_url"
data-image-index="0" class="a-row a-grid-vertical-align a-grid-center ilui-image-input-container image-not-selected hide-remove-button ilui-post-attribute">
<input id="main_image_url" name="main_image_url"
value="" cssClass="image-source-of-truth"
type="hidden" autocomplete="off"
/>
<div class="a-row">
<label for="main_image_url" class="label">
<span class="attribute-label-text">Main Image
<span class="a-size-base a-color-tertiary a-text-italic a-text-normal">
(required)
</span> </span> <span class="a-declarative"
data-action="a-popover" data-a-popover="{"name":"attributeHelpmain_image_url","inlineContent":"The URL where a main image of the product is located. It's important that this is supplied for all products."}">
<span class="help-icon"></span> </span>
</label>
</div>
<div class="a-row ilui-image-wrapper ilui-visualization-element">
<span class="a-declarative" data-action="image-select-main_image_url"
data-image-select-main_image_url="{}"
id="image-container-declarative">
<div tabindex="0" class="ilui-image-select-container">
<div id="image-camera-icon" class="icon-container">
<i class="icon"></i>
<ul class="a-nostyle a-vertical error-list image-error-list"></ul>
</div>
</div>
</span> <img src="" class="ilui-img-thumbnail"
/>
<div class="image-loading-overlay a-hidden">
<div class="a-section upload-progress-meter image-progress-bar">
<div class="a-meter" aria-label="0%">
<div class="a-meter-bar"
style="width: 0%;"></div>
</div> <span class="percentage"></span> </div>
</div> <span class="a-declarative" data-action="image-remove-button-main_image_url"
data-image-remove-button-main_image_url="{}">
<div tabindex="0" class="remove-image-button">
<i class="a-icon a-icon-close"></i>
</div>
</span> </div>
</div>
I'm truing to use materialize css in my project. I load the selectable elements for my select item from the server side, and when data arrived I update the select control with this statement:
$("#surveyTypeId").material_select();
The generated list looks like it contains an additional empty element at the top of the list (the gray area on the image).
When I try to select an item, the option values seems to be shifted. I have the value of the "Classec Survey" item when I select the gray area, I have the value of "Referer Survey about Candidate" when I select "Classic Survey" on the UI.
The generated source is this:
<div class="input-field col s10 m10 l10 no-padding">
<label for="surveyTypeId">Select survey type</label>
<div class="select-wrapper select-material ng-pristine ng-untouched ng-invalid ng-invalid-required initialized">
<span class="caret">▼</span>
<input class="select-dropdown" readonly="true"
data-activates="select-options-995977c5-5d47-4511-fb6a-6a51cbc1cfb6" value=""
type="text">
<ul style="width: 712px; position: absolute; top: 0px; left: 0px; opacity: 1; display: none;" id="select-options-995977c5-5d47-4511-fb6a-6a51cbc1cfb6" class="dropdown-content select-dropdown">
<li class="active"><span></span></li>
<li class=""><span>Classic Survey</span></li>
<li class=""><span>Referrer Survey about Candidate</span></li>
<li class=""><span>Project Manager Survey about Candidate</span></li>
</ul>
<select required="required" id="surveyTypeId" class="select-material
ng-untouched initialized ng-dirty ng-valid-parse ng-valid ng-valid-required" name="surveyTypeId" ng-model="survey.SurveyTypeId" ng-required="true">
<!-- ngRepeat: wl in lookups.SurveyTypesLookupValues -->
<option class="ng-binding ng-scope" ng-repeat="wl in lookups.SurveyTypesLookupValues" value="1">Classic Survey</option>
<!-- end ngRepeat: wl in lookups.SurveyTypesLookupValues -->
<option class="ng-binding ng-scope" ng-repeat="wl in lookups.SurveyTypesLookupValues" value="2">Referrer Survey about Candidate</option>
<!-- end ngRepeat: wl in lookups.SurveyTypesLookupValues -->
<option class="ng-binding ng-scope" ng-repeat="wl in lookups.SurveyTypesLookupValues" value="3">Project Manager Survey about Candidate</option><!-- end ngRepeat: wl in lookups.SurveyTypesLookupValues -->
</select>
</div>
</div>
As you can see I've got only 3 options in the original select control still.
Have you got any idea, why I have the empty item in the materialize generated list? Or how to get rid of that empty item? Or at least how to fix the shift in the values?
Thanks.
SOLVED:
After double checking the angular generation, it turned out, that nitially an empty item is generated as you said. After that the angular update fixed that, but the materialize list generation still kept that item. (Although I did not understand why?) So really the root cause was angular. Thanks to #shashanka n for the idea.