Skip to content
This repository has been archived by the owner on Nov 29, 2018. It is now read-only.

Allow for reuse of the temporary profile, in order to realistically test persistant browser cookies. #1954

Open
lukeis opened this issue Mar 2, 2016 · 17 comments

Comments

@lukeis
Copy link
Member

lukeis commented Mar 2, 2016

Originally reported on Google Code with ID 1954

The test scenario: Creating test cases which test login "Remember me" functionality.
A user of a web site, that has authentication, can choose to store a persistent cookie,
which will allow them to forgo entering their credentials on subsequent visits. The
persistent cookie has an expiration. 
A typical manual test case for this would be to do these steps for each of the browsers
you support:
1. Start the browser, clear cookies, history, cache, etc.
2. Login to the website, choosing "Remember me?", and accepting the persistent cookie.
3. Shut down the browser completely.
4. Start the browser, which still contains the stored cookie.
5. Go to the website, and see that you are logged in automatically, without having
to authenticate with username/password.

In Python WebDriver (and likely the other webdriver clients, and in Selenium RC as
far as I know), the temporary profile gets deleted when the browser session is shut
down.
An automation technique that one might do, would be to collect the cookie value into
a variable when it gets created, then after the browser is shut down, and restarted
with a new clean profile, then set the cookie via the automation code, navigate the
browser to the website, and test the auto-login.  This would simulate the persistent
cookie, but not be the same as truly testing the browser's ability to store the persistent
cookie.

Case in point: The website sets the expiration via the cookie 'Max-Age' attribute,
which is supported in Firefox, and many other browsers. However, Internet Explorer,
all the way through IE9, does not support the 'Max-Age' attribute. IE expects the 'expires'
attribute to be used, in order to set a persistent cookie with an expiration.  A cookie
that gets sent with a 'Max-Age', but no 'expires' attribute, will be accepted for that
session, but will not persist after the browser is shut down.
So, if you were to do the persistent cookie simulation in your automated testing described
above, you would get a false-positive, when running the tests using IE. :(

Using a new clean profile, every time the browser is started, in automated testing
is the best practice.  Sometimes there is a good reason to be able to shut down the
browser and restart it, using the same profile that includes any artifacts or changes
that occurred before the browser was shut down.  

In WebDriver, it is possible to initialize a WebDriver browser session, with a specific
profile. If the quit method can be called with a parameter, that allows you to choose
not reap the profile, then it is possible to initialize the next WebDriver browser
session, using the profile directory and artifacts from the previous session.


Reported by automated.test.spider on 2011-06-29 18:06:51

@lukeis lukeis self-assigned this Mar 2, 2016
@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

Related thread in selenium-users, https://groups.google.com/group/selenium-users/browse_thread/thread/887ba86b91d3cf30?pli=1

Reported by automated.test.spider on 2011-06-29 18:25:01

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

Reported by jmleyba on 2011-06-30 17:28:24

  • Labels added: Type-Enhancement
  • Labels removed: Type-Defect

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

Reported by barancev on 2011-10-13 05:45:03

  • Labels added: Component-WebDriver

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

I have the same issue.

I would like to stress the author's initial description that it is not working for
me to set -firefoxProfileTemplate as template for the new Selenium session profiles
as this defeats the test's purpose.

I have a test case where I want to test the remember me cookie across different browser
session and this means the following:
- First browser session should be a clean/empty FF profile
- The test checks "remember me". The persistent cookie gets created in the profile
- A new browser session is started. This needs to be initialized based on the first
browser's profile. If the first browser failed to create the cookie, I want the test
to fail. So it does not work if I have a ready-made profile with the cookie initialized
and passed with -firefoxProfileTemplate.

I tried some variant of the following workaround (this is on FF 3.6):
- Have an empty profile at a well known location. Pass this with firefoxProfileTemplate
to start the selenium server
- Run the first browser session.
- Copy the session cookie cookies.sqlite from customProfileDir<sessionId> to the profile
template before the temporary profile is deleted.
- Start the new browser session.

This and other variations are quite hacky and rely on Selenium and FF internals. A
cleaner solution where you can pass an argument like: -reuseFirefoxProfile when you
start the server would be great for this scenario and likely others of this kind.

Reported by tpolling21 on 2012-06-26 18:09:00

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

I have the same-ish issue:

Our test website/application takes 30 seconds to log in.  If cookies can be reused,
the login process can be skipped, and the number of tests/minute increases by a factor
of 11 (33 seconds per test -> 3 seconds per test).

I tried getting all cookies, saving them to a file, and reloading them on the next
session.  

Unfortunately, if the cookies are not set at the start, the application immediately
redirects the browser to a login page on a different domain.  This is a problem: when
I then try to add the cookies, Selenium gives an InvalidCookieDomain error.

If Selenium could allow setting cookies for any domain, this could be used to emulate
persistent cookies, which has been effectively outlawed by forcing a blank profile
when starting the browser.

Alternatively, you could create a 'save cookie database' function and then allow users
to optionally specify a cookie database when creating a new browser session.

If this is some kind of security feature (I don't see how: Selenium already has full
control of your browser), you could try restricting 'unlimited cookie creation' to
the time between starting the browser and going to the first url.

If this is a security limitation built into the browser, perhaps modifying the cookie
database on-disk is the (hackish) answer.

Thanks!

Reported by pkts.ca on 2013-03-13 16:20:05

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

I found a workaround!

Initially I thought I'd have to create a page on the server that said "hello world"
and just sat there, so that I could add the cookies for that domain.  Then I realized
that all of the images were already there and static, so I tried that, but got the
error that cookies can only be set on html pages.  It turns out that the '403 Forbidden'
page is an html page, so trying to go to the directory containing the icons generated
a satisfactory page for loading cookies with selenium; the same is probably true for
'404 Not found' pages, but I didn't try that.

Thanks!

Reported by pkts.ca on 2013-03-13 17:25:50

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

I am running Selenium in a non-testing context (to drive a browser that runs as a chat
bot on a dedicated server), and the absence of a persistent profile means that the
bot can't take advantage of persistent HTML5 storage. I actually don't care if I lose
cookies, because I can login again to the service and obtain a new cookie each time
I start up Selenium, but I lose all changes to the HTML5 storage, which is mutated
quite frequently.

I can work on some code adding a capability to FirefoxDriver that turns on/off the
temporary profile copying behavior. But it isn't worth it to me to do that if it will
not be accepted by upstream. I believe I could implement a hack that isn't good enough
quality to be accepted upstream in less time than it would take me to add a proper
capability to FirefoxDriver. If upstream isn't interested in this, I will save my time,
otherwise I will work on the feature and polish it and test it to make sure that it
is good quality code and reliable. Please let me know.

Reported by SMcNam on 2013-10-05 22:14:59

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

@SMcNam: You can reuse a named profile that was created manually.

Reported by barancev on 2013-10-07 11:32:33

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

While it's true that you can reuse a named profile, there is currently no way to **write**
any modifications made by the browser being automated by Selenium, back to the original
profile.

Indeed, the FirefoxProfile.layoutOnDisk() method -- here http://code.google.com/p/selenium/source/browse/java/client/src/org/openqa/selenium/firefox/FirefoxProfile.java#435
-- **unconditionally** calls copyModel(), which places a copy of the profile directory
in the system's temp directory. When the Selenium JVM exits, the copy is deleted. I
want Selenium to modify the profile specified in the FirefoxProfile constructor **directly**
in the exact folder it is in, without making a copy. From my reading of the source,
this is currently impossible without modifying the source code of Selenium. I have
been unable to compile Selenium successfully on any platform -- Mac, Windows or Linux
-- and there appear to be no formal build instructions, but I did manage to make a
change to FirefoxProfile.java that appears to implement what I desire. I'll work on
building it and test my changes.

Reported by SMcNam on 2013-10-07 13:54:43

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

True, Selenium makes a copy of the profile. It seems we need an option to use a named
profile directly, without copying it.

Reported by barancev on 2013-10-07 18:30:24

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

Attaching a patch to FirefoxProfile.java that adds a new constructor, FirefoxProfile(File,
boolean) -- if you pass true for the boolean value, the layoutOnDisk method does not
create a temporary copy of the profile, and returns the same path as passed in the
constructor, so the same profile directory gets used again and again. Useful if you
want to retain ALL profile data between Selenium processes: HTML5 storage, cookies,
everything.

Reported by SMcNam on 2013-10-11 02:58:08


- _Attachment: [optionally-persist-firefox-profile.txt](https://storage.googleapis.com/google-code-attachments/selenium/issue-1954/comment-11/optionally-persist-firefox-profile.txt)_

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

Newbie question that would be very helpful if answered.

The attached code looks great, editing FirefoxProfile.java  But if I cannot build the
project (having same issue as you did), how can I use the edited file...to get a DLL
or something that will let me implement this. Thank you very much for any help.

Reported by writejohnemail on 2014-01-21 22:35:23

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

I am working on a project that needs to build a database of third party tracking cookies.
I would like to use selenium for this as well, but I need to be able to extract cookies
and history after the selenium script has run, I also may need to use this same profile
(with cookies and history) in future tests.  The solution that @barancev proposed would
be perfect!

Reported by cooperq on 2014-05-13 23:23:43

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

Those who think this feature is helpful, should add their comments on below thread.

https://code.google.com/p/selenium/issues/detail?id=7374&

This is a C# patch only for now which allows picking up an existing profile on system
and uses the same

Reported by tarunlalwani on 2014-05-27 07:52:36

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

Rather old thread, but the subject is still pertinent (selenium 2.46).

In my case, I want to keep cookies persistent across invocations.  There will never
be more than one instance of this particular selenium script running concurrently,
so pointing ff at the same profile should be okay.

The use case is automating the fetching of data from a website that requires login
each session (no 'remember me' option), and changes at least one of the persistent
auth cookies on every invocation.

Here is a python contribution, similar to #14.  At this point (to the extent that I've
tested it) the attached code seems to do the job.

class FirefoxProfileInPlace is a drop-in replacement for and with identical default
behaviour to class FirefoxProfile.  In order to create a profile object that uses the
ff profile in place, the in_place = True arg needs to be provided to the constructor.

Reported by strix.avfm on 2015-07-16 15:31:03


- _Attachment: [FirefoxProfileInPlace.py](https://storage.googleapis.com/google-code-attachments/selenium/issue-1954/comment-15/FirefoxProfileInPlace.py)_

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

So, after a bit more testing, what I submitted seems to work _but_ there is one wrinkle:
firefox_binary.py doesn't check to see whether the x86 or amd64 directories exist before
trying to create them.  Therefore, something similar to the following is required:

    for x in ['x86', 'amd64']:
        try:
            shutil.rmtree( os.path.join( ffp_path, x ) )
        except OSError:
            pass

where ffp_path is the path to the profile in question.

Whether this is a Selenium bug (because such directories could have been created by
other binary plugins), idk.

Reported by strix.avfm on 2015-07-16 21:40:27

@lukeis
Copy link
Member Author

lukeis commented Mar 2, 2016

Reported by luke.semerau on 2015-09-17 17:44:24

  • Labels added: Restrict-AddIssueComment-Commit

@SeleniumHQ SeleniumHQ locked and limited conversation to collaborators Mar 4, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant