I have a page that is for booking an appointment and it has some javascript code that selects the earliest time and day when the appointment is available, after which user can click on the button to schedule the appointment.
So in order to test that, I was writing some rspec test like the following,
book_appointment_spec.rb
context "when on the profile page" do
before do
click_linkedin_button
end
it 'book an appointment', :js => true do
click_link "new-appointment"
choose "doctor1"
click_button "Submit and Schedule"
expect(page).to have_content "Congrats!"
end
end
click_linkedin_button is a method definition that just logins a user via linkedin oauth. The problem is that even after setting OmniAuth.config.mock_auth[:linkedin], whenever I set :js => true around it block, it asks me to login via linkedin, http://imgur.com/mYUOxgD
I was wondering if anyone knows how to fix this problem.
Following are other files that might be relevant to this problem.
spec_helper.rb
require 'capybara/webkit/matchers'
Capybara.javascript_driver = :webkit
Gemfile
gem 'capybara-webkit'
gem 'database_cleaner'
gem 'capybara'
As you've discoverd, you can't run part of a test using one driver and part of the test using another. This would be equivalent to saying
Given I login in using Safari.
Then I should be logged in using Firefox.
So your solution is that you have to run your js login code in the test environment to login. This is actually a good thing (you want to test your js login code). If you want to avoid actually connecting to linkedin, every time you run this test, then you need to mock the connection to linkedin. Have a look at VCR (https://github.com/vcr/vcr), this will allow you to record your connection to linkedin, so that in subsequent test runs you don't have to goto linkedin.
Setting js: true in your rspec block is a shortcut to use the javascript-enabled driver for the whole example. So this driver will be available and used during the whole execution or the example, which includes all before/after/around blocks.
To work around this, instead of using js: true, you can manually set which driver to use at the point(s) of your example where you need to.
it {
do_some_stuff_with_default_driver
Capybara.current_driver = :webkit
do_some_stuff_with_webkit
Capybara.current_driver = :selenium
do_some_stuff_with_selenium
}
EDIT
Oops I just read this, so perhaps that solution will not be working. please let me know
Note: switching the driver creates a new session, so you may not be able to switch in the middle of a test.
I solved it by creating an actual linkedin account, put that auth info in the .env and just called fill_in method to fill out email and password field during its callback. And stuck with running a js driver throughout the entire context block.
EDIT: This is of course not the best answer so I am still accepting other answers.
Related
I have cable internet which requires me to login though a web page. It's annoying how it resets every day at 8 and 12am. I wanted to write a python script which will automate the login process. I've read many StackOverflow solutions so far, nothing has worked. I have tried Requests, Twill, RoboBrowser etc.
Upon inspecting the page source I came across a doLogin() ajax script, which is triggered by login button. Here is the full page source.
following is one of my implementations which fails
import requests
# Fill in your details here to be posted to the login form.
payload = {
'action': 'http://10.10.0.1/login',
'actualusername': 'username',
'actualpassword': 'password'
}
# Use 'with' to ensure the session context is closed after use.
with requests.Session() as s:
p = s.post("http://103.251.83.134/captiveportal/Default.aspx", data=payload)
# print the html returned or something more intelligent to see if it's a successful login page.
print p.text
# An authorised request.
#r = s.get('http://www.google.com')
#print r.text
EDIT: Solution
I used Selenium WebDriver to fix this. Check answer.
Use Selenium :) Download ChromeDriver to the path, make a two-time variable and check the time every minute. If it's 'login time', your browser will pass through the authorization.
from selenium import webdriver
def Authorization_for_broadband():
driver = webdriver.Chrome("C:\YOURPATHTO\CHROMEDRIVER.EXE")
driver.get('http://10.10.0.1/login')
driver.find_element_by_xpath('//*[#id="username"]').send_keys('USERNAME')
driver.find_element_by_xpath('//*[#id="password"]').send_keys('PASSWORD')
driver.find_element_by_xpath('//*[#id="btnLogin"]').click()
driver.close
while(1):
if time=='your-login-period1' or time == 'your-login-period2':
Authorization_for_broadband()
Your URL may be wrong. Looking at the source code it looks like the HTML form is posting data to the http://10.10.0.1/login page and then the doLogin() function is submitting data to Register.aspx?CheckCustomerStatus=1.
Also your payload includes the variable action and you're using a Session object, which I don't think is necessary.
I can't test it since it's a local login page I can't access, but I would try modifying your code to submit the login info to both pages using a simpler POST request
I've seen many threads about it but cannot find a satisfying answer: when using the Google sign-in button (https://developers.google.com/identity/sign-in/web/sign-in), is it possible to already have the authorizations accepted ? Like if I add the client ID of my app somewhere in the Google console ?
For now I'm calling the auth2.grantOfflineAccess when clicking the button (so I can pass the returned code to my backend and make sure the user is from the expected domain).
If you're able to answer the first question and - bonus point - know if what I'm doing after clicking the button is right, you'd be awesome !
Thanks to Steven's comment, I'm now able to have the authorizations accepted by default. Be aware there will still be a second popup (after the one that requests your email and password) to inform you that your admin has granted the app to access your data. Only at your first connection though.
So what you need to do is to follow the third step of this document. They say you only need the plus.me and userinfo.email scopes if you only request the basic profile of the user but it was not working in my case, I also needed the userinfo.profile scope (because I use grantOfflineAccess() ?).
I have an application that has a simple form submission (an email). I have MixPanel's tracking analytics integrated into the app, and want to track every time an interaction with this form happens--specifically, when a user successfully/unsuccessfully submits the form, and what their email is.
After doing some research, it seems simplest method is to track at the controller level (where I am running validations, and making the overall decision whether or not the submission is legitimate). This would require running a JS function from the controller. Is this possible? Is there a better method of doing this?
Let integrate this gem analytical.
Then just follow the instruction in above link, but here is the summary:
Add analytical to your controller
Setup mixpanel token in config/analytical.yml
production:
mixpanel:
key: your_mixpanel_project_token
development:
# Also define here if you want to test in development
test:
Track your event in controller:
# data_key_1, data_value_1, data_key_2 and data_value_2 are the data
# which were attached to mixpanel event, they are example, specify yours
analytical.event 'Your event', data_key_1: data_value_1, data_key_2: data_value_2
You may integrate more with GA, KISSMetrics, or whatever you want, the tracking code works in generic, this also works with javascript code, for more detail, please refer its documentation.
i have some scripts which i have to execute from my ruby on rails application. To ensure that scripts do what they should, my application must show/tail the content of logfiles which where generated from the scripts.
In more detail: i have expect scripts which configure some blackbox devices over a seriell connection (some sort of a rollout mechanism). So i have to watch, for example, a update process or a reboot of the connected device (to verify that everything is okay). This is what i write to my logfiles.
Therefore i need to:
execute a process and handle the exit code
tail a/some logfiles (maybe Javascript or html5?)
How could i do that? Examples will be really appreciate!
Thanks a lot!
The answer to #1 is pretty easy, the system call, i.e.
ret = system('ls','-l')
ret will be true if the command had a zero exit status. $? will contain a Process::Status object from which you can obtain the exit status
unless system('ls','-l','/a_bogus_dir')
logger.debug("ls failed with #{$?.exitstatus}")
end
The answer for #2 can be done several ways. You could create a controller action that simply grabbed the contents of a specific file in the filesystem, and returned the contents.
def get_file_contents
File.open(params[:file_to_read],"r") { |f| #contents = f.read }
respond_to |format|
format.js
end
end
Then create the file get_file_contents.js.erb:
$('#display_div').html('<%= escape_javascript(#contents) %>');
Then you'd have to create a timer of some kind on your page to repeatedly call that controller action, I use jquery.timers. In a timer loop you would call
$.get('/get_file_contents?file_to_read=public/logfile');
That will hit the controller, grab the file contents, and execute get_file_contents.js.erb, which would update the div with the current contents of the file.
You'd have to add the route /get_file_contents to routes.rb.
I'm getting conflicting results between the facebook javascript SDK and the python requesthandler variables. The Javascript SDK says my user is not logged in, which is correct, while my template variable that comes from the base request handler says that my user is logged in and displays the name of the user. Is there enough info to tell what is wrong or should I paste the code I think is relevant here? A link to the login page that has the error is here. The example I used is called the runwithfriends demo app from facebook and everything with that app worked except using the logic from the app just from a website without requiring the user to be in the iframe of the app.
Plus I can't seem to get the real-time API working. I can only save userID and not refresh user data - why? I have the code but I'm not sure what's most relevant but here's some of the request handler, the relevant code is basically exactly the same as the one from the demo app:
def render(self, name, **data):
logging.debug('render')
"""Render a template"""
if not data:
logging.debug('no data')
data = {}
data[u'js_conf'] = json.dumps({
u'appId': facebookconf.FACEBOOK_APP_ID,
u'canvasName': facebookconf.FACEBOOK_CANVAS_NAME,
u'userIdOnServer': self.user.id if self.user else None,
})
data[u'logged_in_user'] = self.user #variable that is the problem
data[u'message'] = self.get_message()
data[u'csrf_token'] = self.csrf_token
data[u'canvas_name'] = facebookconf.FACEBOOK_CANVAS_NAME
self.response.out.write(template.render(
os.path.join(
os.path.dirname(__file__), 'templates', name + '.html'),
data))
And even more strange, I can also get the application in a state where the javascript SDK says the user is logged in and the template variable logged_in_user says otherwise. Why are the variables conflicting?
Update: Here are screenshots from the strange login flow. I can go to my page and my name from facebook appears:
Then when I go to next page it also looks alright and has my name
But if I log out then I gets in impossible state: my name + logged out
How can I resolve this strange conflict between js and back-end?
Update: Since I only have this problem for one of my apps I can take what works from my other app and integrate. This page seems to work from my other app: http://cyberfaze.appspot.com/file/20985
Your 'user' is probably referring to the Django user not the Facebook user. Make sure you synchronize the two accounts correctly using a custom authentication backend. It's possible that the accounts get out of sync i.e. if the user switches browsers.
Keep in mind that the Facebook Python SDK will stop working after October 1st unless they update it to Oauth2.0 which is unlikely.
I just updated django-facebook-graph to work with the new authentication flow.