I am working on a program for downloading bills that will automatically navigate to a webpage using the web browser object, log into it, navigate to the Bills page and download the most recent bill.
It all works well up until where I actually download it, as using the invokemember("click") code fires up IE and asks me to log in again which is not what I want to do. The DownloadFile method also does not work, and downloads a file that says the object has been moved to he login URL.
Right clicking and selecting "save target as" from the web browser works, but I'm not sure how to go about automating it.
Edit: Here's the code for the downloading part, parts of it were borrowed from another question on here but I can't remember who or where.
Dim IsRightElement As Boolean = False
For Each curElement As HtmlElement In Browser.Document.Links()
If curElement.GetAttribute("InnerText") = "Download Call Charges" Then
IsRightElement = True
End If
If IsRightElement Then
Dim Link As String = curElement.DomElement.href.ToString()
'This is where I'm stuck
'My.Computer.Network.DownloadFile(Link, "C:\Users\user\Desktop\PhoneBill.csv", "<username>", "<password>")
'The above does not work
'curElement.InvokeMember("contextmenu")
'Not sure what to do here
IsRightElement = False
Exit For
End If
Next
Me.Close()
Related
I'm writing a simple Automator script in Javascript.
I want to send a key-code(or key-storke) to an OS X application that is not in front.
Basically, I want to run this code and do my things while the script opens a certain application, write text, and hit enter - all of this without bothering my other work.
I want something like this:
Application("System Events").processes['someApp'].windows[0].textFields[0].keyCode(76);
In Script Dictionary, there is keyCode method under Processes Suite.
The above code, however, throws an error that follows:
execution error: Error on line 16: Error: Named parameters must be passed as an object. (-2700)
I understand that the following code works fine, but it require the application to be running in front:
// KeyCode 76 => "Enter"
Application("System Events").keyCode(76);
UPDATE: I'm trying to search something on iTunes(Apple Music). Is this possible without bringing iTunes app upfront?
It's possible to write text in application that is not in front with the help of the GUI Scripting (accessibility), but :
You need to know what UI elements are in the window of your specific
application, and to know the attributes and properties of the
specific element.
You need to add your script in the System Preferences --> Security
& Privacy --> Accessibility.
Here's a sample script (tested on macOS Sierra) to write some text at the position of the cursor in the front document of the "TextEdit" application.
Application("System Events").processes['TextEdit'].windows[0].scrollAreas[0].textAreas[0].attributes["AXSelectedText"].value = "some text" + "\r" // r is the return KEY
Update
To send some key code to a background application, you can use the CGEventPostToPid() method of the Carbon framework.
Here's the script to search some text in iTunes (Works on my computer, macOS Sierra and iTunes Version 10.6.2).
ObjC.import('Carbon')
iPid = Application("System Events").processes['iTunes'].unixId()
searchField = Application("System Events").processes['iTunes'].windows[0].textFields[0]
searchField.buttons[0].actions['AXPress'].perform()
delay(0.1) // increase it, if no search
searchField.focused = true
delay(0.3) // increase it, if no search
searchField.value = "world" // the searching text
searchField.actions["AXConfirm"].perform()
delay(0.1) // increase it, if no search
// ** carbon methods to send the enter key to a background application ***
enterDown = $.CGEventCreateKeyboardEvent($(), 76, true);
enterUp = $.CGEventCreateKeyboardEvent($(), 76, false);
$.CGEventPostToPid(iPid, enterDown);
delay(0.1)
$.CGEventPostToPid(iPid, enterUp);
Here is the issue that has been nagging for weeks and all solutions found online do not seem to work... ie. wait for ajax, etc...
here is versions of gems:
capybara (2.10.1, 2.7.1)
selenium-webdriver (3.0.1, 3.0.0)
rspec (3.5.0)
running ruby 2.2.5
ruby 2.2.5p319 (2016-04-26 revision 54774) [x64-mingw32]
in the env.rb
Capybara.register_driver :selenium do | app |
browser = (ENV['browser'] || 'firefox').to_sym
Capybara::Driver::Selenium.new(app, :browser => browser.to_sym, :resynchronize => true)
Capybara.default_max_wait_time = 5
end
Here is my dynamicpage.feature
Given I visit page X
Then placeholder text appears
And the placeholder text is replaced by the content provided by the json service
and the step.rb
When(/^I visit page X$/) do
visit('mysite.com/productx/')
end
When(/^placeholder text appears$/) do
expect(page).to have_css(".text-replacer-pending")
end
Then(/^the placeholder text is replaced by the content provided by the json service$/) do
expect(page).to have_css(".text-replacer-done")
end
the webpage in question, which I cannot add it here as it is not publicly accessible, contains the following on page load:
1- <span class="text-replacer-pending">Placeholder Text</span>
after a call to an external service (which provides the Json data), the same span class gets refreshed/updated to the following;
2- <span class="text-replacer-done">Correct Data</span>
The problem I have with the "visit" method in capybara + selenium is that as soon as it visits the page, it thinks everything loaded and freezes the browser, and it never lets the service be called to dynamically update the content.
I tried the following solutions but without success:
Capybara.default_max_wait_time = 5
Capybara::Driver::Selenium.new(app, :browser => browser.to_sym, :resynchronize => true)
add sleep 5 after the visit method
wait for ajax solution from several websites, etc...
adding after hooks
etc...
I am at a complete loss why "visit" can't wait or at least provide a simple solution to an issue i am sure is very common.
I am aware of the capybara methods that wait and those that don't wait such as 'visit' but the issue is;
there is no content that goes from hidden to displayed
there is there is no user interaction either, just the content is getting updated.
also unsure if this is a capybara issue or a selenium or both.
Anyhow have insight on any solutions? i am fairly new to ruby and cucumber so specifically what code goes in what file/folder would be much appreciated.
Mel
Restore wait_until method (add it to your spec_helpers.rb)
def wait_until(timeout = DEFAULT_WAIT_TIME)
Timeout.timeout(timeout) do
sleep(0.1) until value = yield
value
end
end
And then:
# time in seconds
wait_until(20) { has_no_css?('.text-replacer-pending') }
expect(page).to have_css(".text-replacer-done")
#maxple and #nattf0dd
Just to close the loop on our issue here...
After looking at this problem from a different angle,
we finally found out Cucumber/Capybara/ is not a problem at all :-)
The issue we are having lies with the browser Firefox driver (SSL related), since we have no issues when running the same test with the Chrome driver.
I do appreciate the replies and suggestions and will keep those in mind for future.
thanks again!
Follow the following steps.
Steps 1:
Go to google.
Open the javascript console.
Enter the command: document.all.q.value = "hello"
As expected the element with a name of "q" (the search field) is set to "hello").
Steps 2:
Go to google.
In the address bar type javascript: document.all.q.value = "hello!"
Press Enter
If your browser is either Internet Explorer, or Google Chrome, the javascript will have replaced the google website with an entirely blank page, with the exception of the word "Hello".
Finally
Now that you've bugged out your browser, go back to Google.com and repeat Steps 1. You should receive an error message "Uncaught ReferenceError: document is not defined (...) VM83:1
Question:
Am I doing something wrong? And is there another method which works, while still using the address bar for JS input?
The purpose of a javascript: scheme URL is to generate a new page using JavaScript. Modifying the existing page with it is something of a hack.
document.all.q.value = "hello!"; evalues as "hello!", so when you visit that URL, a new HTML document consisting solely of the text hello! is generated and loaded in place of the existing page.
To avoid this: Make sure the JS does not return a string. You can do this by using void.
javascript:void(document.all.q.value = "hello!");
When messing around with javascript: in the adressbar some (if not the most) browsers handle it as a new page, so you have to add a window.history.back(); at the end
javascript: document.all.q.value = "hello!"; window.history.back();
I've got got a page with some AJAX code that I'm trying to build a spec for using Capybara. This test works:
context 'from the JobSpec#index page' do
scenario 'clicks the Run now link' do
visit job_specs_path
within( find('tr', text: #job_spec.name) ) { click_link 'run_now' }
visit current_path
expect(find('tr', text: #job_spec.name)).to have_content(/(running|success)/)
end
end
After clicking the link 'run_now', a job is launched and the user is redirected to the launched_jobs_path, which has a Datatable that uses some AJAX to pull currently running jobs. This test passes. However, I wanted to add to the test to check to make sure that the job wasn't already running before clicking the 'run_now' button (which would give me a false positive). I started playing around with it, but then I noticed that even simply putting visit launched_jobs_path before visit job_specs_path would cause the test to fail with the error
Failure/Error: expect(find('tr', text: #job_spec.name)).to have_content(/(running|success)/)
Capybara::ElementNotFound:
Unable to find css "tr" with text "job_spec_collection-1"
I'm fairly certain that this is an issue with the Javascript not running at the right time, but I don't quite know how to resolve it. Any ideas? This is Rails 4.1 with Capybara 2.4, and Capybara-webkit 1.3.
Thanks!
Update
In the parent context of the spec, I do have :js => true
feature 'User runs a JobSpec now', :js => true do
before do
Capybara.current_driver = :webkit
#job_spec = FactoryGirl.create(:job_spec, :with_client_spaces)
end
after { Capybara.use_default_driver }
context 'from the JobSpec#index page' do
...
end
end
I think it might be as simple as adding :js => true to your scenario header so that it knows to launch webkit / selenium to test the js / ajax.
scenario 'clicks the Run now link', :js => true do
Hope that helps!
UPDATE
Well here's one more idea. You could try adding this to your 'rails_helper' or 'spec_helper' file to set the :js driver.
Capybara.javascript_driver = :webkit
Do you have other :js tests that are working properly with :webkit? Have you tried using save_and_open_page to see if the element you are looking for is actually on the page and Capybara just isn't finding it?
Ok, pretty sure I've got something that will at least work for now. If anyone has more elegant solutions I'd love to hear them.
I think the issue is in the complexity of what happens in the 'run_now' controller. It adds a job to a Rufus scheduler process, which then has to start the job (supposed to start immediately). Only after the launched job is running via Rufus does an entry in the launched_jobs table get created.
While developing, I would see the job just launched as soon as I clicked 'Run now'. However, I'm guessing that WebKit is so fast that the job hasn't been launched by the time it gets to the launched_jobs#index page. The extra visit current_path there seem to help at first because it would refresh the page, but it only worked occasionally. Wrapping these last two steps in a synchronize block seems to work reliably (I've run it about 20 times now without any failures):
context 'from the JobSpec#index page' do
scenario 'clicks the Run now link' do
# First make sure that the job isn't already running
visit launched_jobs_path
expect(page).to have_no_selector('tr', text: #job_spec.name)
# Now launch the job
visit job_specs_path
within( find('tr', text: #job_spec.name) ) { click_link 'run_now' }
page.document.synchronize do
visit current_path
expect(find('tr', text: #job_spec.name)).to have_content(/(running|success)/)
end
end
end
I am coding a small app ,in middle i struck at some point where i have to execute javascript to get my data ?
in my process ,i have to login to some url and then go to some page and have to get data from that . i done all this with indy idhttp ,i got all info except one column which needs javascript to get value ,then i tried using twebbowser to make it work for me ,but how can i use cookies to enabled to webbrowser ?
i navigated browserto('http://mysite.com/login.php user and pass ') ,well its loged in and then i tried to access next link like ('http://mysite.com/link1/example.php')but it directing to login page :(
any help appreciated :)
What is your question now? In the title you ask how to execute JavaScript. Try the following:
uses
MSHTML_TLB, SHDocVw, ShellAPI;
function ExecuteScript(doc: IHTMLDocument2; script: string; language: string): Boolean;
var
win: IHTMLWindow2;
Olelanguage: Olevariant;
begin
if doc <> nil then
begin
try
win := doc.parentWindow;
if win <> nil then
begin
try
Olelanguage := language;
win.ExecScript(script, Olelanguage);
finally
win := nil;
end;
end;
finally
doc := nil;
end;
end;
end;
Sample usage:
IDoc: IHTMLDocument2;
Webbrowser1.Document.QueryInterface(IHTMLDocument2, iDoc);
ExecuteScript(iDoc, 'document.login.submit()', 'JavaScript');
(This, and more, can be found here).
Then in the text you ask how to use cookies (when using TWebBrowser this should happen automatically). When using Indy HTTP, you just have to attach a TIdCookieManager to your TIdHTTPClient instance, thats all (but you probably don't want to use that anyway, due to the script requirement....)
Your best bet would be to automate IE itself. Grab a copy of embeddedwb, drop on a form and navigate to the url which you need to execute something. There is a document property of the component which returns an OLEVariant, use this to execute a DHTML style statement.. something like document.form.submit;.
You can easily hide the window used for automation, one technique I have used is to place it on a new page on a page control, add a second page to display the status, then show the status page and hide the tabs.