Closed ghost closed 6 years ago
First of all, what settings do I have to provide to geckodriver to disable auto-update of the firefox on startup? Since firefox 56.0.2 it would update on startup and fail all the tests because of that.
First of all it would be helpful if you didn’t coalesce a bunch of different issues together when filing a bug.
Unless you’re passing in a custom profile it is surprising to me that Firefox autoupdates. Can you file a separate bug about this?
A website with 3 angular 2 routes : localhost/#/login, localhost/#/home, localhost/#/page Login func is on basic auth (if that matters), and pages contain some unique elements. Also, upon successful login, it redirects automatically to #home
One observation is that basic auth isn’t supported by WebDriver yet. See https://github.com/w3c/webdriver/issues/385.
In any case it would be helpful if you provided a reduced test case that was self-contained (e.g. didn’t involve Angular or any other dependencies, such as Selenium).
The only thing I can see from the trace log is that you navigate to https://localhost:8080/#/page
, for which popstate
fires and geckodriver returns. You then inject a long and complicated JavaScript that I don’t understand what does. Then you begin what looks to be an expected condition checking the current URL, which then appears to be https://localhost:8080/#/login
.
1515700606200 webdriver::server DEBUG -> POST /session/82fe6933-c247-4530-b781-f6d6f45cbac4/url {"url":"https://localhost:8080/#/page"}
1515700606200 geckodriver::marionette TRACE -> 61:[0,95,"get",{"url":"https://localhost:8080/#/page"}]
1515700606211 Marionette TRACE 0 -> [0,95,"get",{"url":"https://localhost:8080/#/page"}]
1515700606220 Marionette DEBUG Received DOM event "popstate" for "https://localhost:8080/#/page"
1515700606220 Marionette TRACE 0 <- [1,95,null,{}]
1515700606215 geckodriver::marionette TRACE <- [1,95,null,{}]
1515700606215 webdriver::server DEBUG <- 200 OK {"value": {}}
1515700606226 webdriver::server DEBUG -> POST /session/82fe6933-c247-4530-b781-f6d6f45cbac4/execute/async {"script":"var callback = arguments[arguments.length - 1];\nvar rootSelector = 'null';\n\nvar getNg1Hooks = function(selector, injectorPlease) {\r\n function tryEl(el) {\r\n try {\r\n if (!injectorPlease && angular.getTestability) {\r\n var $$testability = angular.getTestability(el);\r\n if ($$testability) {\r\n return {$$testability: $$testability};\r\n }\r\n } else {\r\n var $injector = angular.element(el).injector();\r\n if ($injector) {\r\n return {$injector: $injector};\r\n }\r\n }\r\n } catch(err) {}\r\n }\r\n function trySelector(selector) {\r\n var els = document.querySelectorAll(selector);\r\n for (var i = 0; i < els.length; i++) {\r\n var elHooks = tryEl(els[i]);\r\n if (elHooks) {\r\n return elHooks;\r\n }\r\n }\r\n }\r\n\r\n if (selector) {\r\n return trySelector(selector);\r\n } else if (window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__) {\r\n var $injector = window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__;\r\n var $$testability = null;\r\n try {\r\n $$testability = $injector.get('$$testability');\r\n } catch (e) {}\r\n return {$injector: $injector, $$testability: $$testability};\r\n } else {\r\n return tryEl(document.body) ||\r\n trySelector('[ng-app]') || trySelector('[ng\\\\:app]') ||\r\n trySelector('[ng-controller]') || trySelector('[ng\\\\:controller]');\r\n }\r\n}\n\r\n\r\n try {\r\n // Wait for both angular1 testability and angular2 testability.\r\n\r\n var testCallback = callback;\r\n\r\n // Wait for angular1 testability first and run waitForAngular2 as a callback\r\n var waitForAngular1 = function(callback) {\r\n\r\n if (window.angular) {\r\n var hooks = getNg1Hooks(rootSelector);\r\n if (!hooks){\r\n callback(); // not an angular1 app\r\n }\r\n else{\r\n if (hooks.$$testability) {\r\n hooks.$$testability.whenStable(callback);\r\n } else if (hooks.$injector) {\r\n hooks.$injector.get('$browser')\r\n .notifyWhenNoOutstandingRequests(callback);\r\n } else if (!!rootSelector) {\r\n throw new Error(\r\n 'Could not automatically find injector on page: \"' +\r\n window.location.toString() + '\". Consider using config.rootEl');\r\n } else {\r\n throw new Error(\r\n 'root element (' + rootSelector + ') has no injector.' +\r\n ' this may mean it is not inside ng-app.');\r\n }\r\n }\r\n }\r\n else {callback();} // not an angular1 app\r\n };\r\n\r\n // Wait for Angular2 testability and then run test callback\r\n var waitForAngular2 = function() {\r\n if (window.getAngularTestability) {\r\n if (rootSelector) {\r\n var testability = null;\r\n var el = document.querySelector(rootSelector);\r\n try{\r\n testability = window.getAngularTestability(el);\r\n }\r\n catch(e){}\r\n if (testability) {\r\n return testability.whenStable(function() { testCallback(); });\r\n }\r\n }\r\n\r\n // Didn't specify root element or testability could not be found\r\n // by rootSelector. This may happen in a hybrid app, which could have\r\n // more than one root.\r\n var testabilities = window.getAllAngularTestabilities();\r\n var count = testabilities.length;\r\n\r\n // No angular2 testability, this happens when\r\n // going to a hybrid page and going back to a pure angular1 page\r\n if (count === 0) {\r\n return testCallback();\r\n }\r\n\r\n var decrement = function() {\r\n count--;\r\n if (count === 0) {\r\n testCallback();\r\n }\r\n };\r\n testabilities.forEach(function(testability) {\r\n testability.whenStable(decrement);\r\n });\r\n\r\n }\r\n else {testCallback();} // not an angular2 app\r\n };\r\n\r\n if (!(window.angular) && !(window.getAngularTestability)) {\r\n // no testability hook\r\n throw new Error(\r\n 'both angularJS testability and angular testability are undefined.' +\r\n ' This could be either ' +\r\n 'because this is a non-angular page or because your test involves ' +\r\n 'client-side navigation, which can interfere with Protractor\\'s ' +\r\n 'bootstrapping. See http://git.io/v4gXM for details');\r\n } else {waitForAngular1(waitForAngular2);} // Wait for angular1 and angular2\r\n // Testability hooks sequentially\r\n\r\n } catch (err) {\r\n callback(err.message);\r\n }\r\n\r\n","args":[]}
1515700606226 geckodriver::marionette TRACE -> 4945:[0,96,"executeAsyncScript",{"args":[],"newSandbox":false,"script":"var callback = arguments[arguments.length - 1];\nvar rootSelector = 'null';\n\nvar getNg1Hooks = function(selector, injectorPlease) {\r\n function tryEl(el) {\r\n try {\r\n if (!injectorPlease && angular.getTestability) {\r\n var $$testability = angular.getTestability(el);\r\n if ($$testability) {\r\n return {$$testability: $$testability};\r\n }\r\n } else {\r\n var $injector = angular.element(el).injector();\r\n if ($injector) {\r\n return {$injector: $injector};\r\n }\r\n }\r\n } catch(err) {}\r\n }\r\n function trySelector(selector) {\r\n var els = document.querySelectorAll(selector);\r\n for (var i = 0; i < els.length; i++) {\r\n var elHooks = tryEl(els[i]);\r\n if (elHooks) {\r\n return elHooks;\r\n }\r\n }\r\n }\r\n\r\n if (selector) {\r\n return trySelector(selector);\r\n } else if (window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__) {\r\n var $injector = window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__;\r\n var $$testability = null;\r\n try {\r\n $$testability = $injector.get('$$testability');\r\n } catch (e) {}\r\n return {$injector: $injector, $$testability: $$testability};\r\n } else {\r\n return tryEl(document.body) ||\r\n trySelector('[ng-app]') || trySelector('[ng\\\\:app]') ||\r\n trySelector('[ng-controller]') || trySelector('[ng\\\\:controller]');\r\n }\r\n}\n\r\n\r\n try {\r\n // Wait for both angular1 testability and angular2 testability.\r\n\r\n var testCallback = callback;\r\n\r\n // Wait for angular1 testability first and run waitForAngular2 as a callback\r\n var waitForAngular1 = function(callback) {\r\n\r\n if (window.angular) {\r\n var hooks = getNg1Hooks(rootSelector);\r\n if (!hooks){\r\n callback(); // not an angular1 app\r\n }\r\n else{\r\n if (hooks.$$testability) {\r\n hooks.$$testability.whenStable(callback);\r\n } else if (hooks.$injector) {\r\n hooks.$injector.get('$browser')\r\n .notifyWhenNoOutstandingRequests(callback);\r\n } else if (!!rootSelector) {\r\n throw new Error(\r\n 'Could not automatically find injector on page: \"' +\r\n window.location.toString() + '\". Consider using config.rootEl');\r\n } else {\r\n throw new Error(\r\n 'root element (' + rootSelector + ') has no injector.' +\r\n ' this may mean it is not inside ng-app.');\r\n }\r\n }\r\n }\r\n else {callback();} // not an angular1 app\r\n };\r\n\r\n // Wait for Angular2 testability and then run test callback\r\n var waitForAngular2 = function() {\r\n if (window.getAngularTestability) {\r\n if (rootSelector) {\r\n var testability = null;\r\n var el = document.querySelector(rootSelector);\r\n try{\r\n testability = window.getAngularTestability(el);\r\n }\r\n catch(e){}\r\n if (testability) {\r\n return testability.whenStable(function() { testCallback(); });\r\n }\r\n }\r\n\r\n // Didn't specify root element or testability could not be found\r\n // by rootSelector. This may happen in a hybrid app, which could have\r\n // more than one root.\r\n var testabilities = window.getAllAngularTestabilities();\r\n var count = testabilities.length;\r\n\r\n // No angular2 testability, this happens when\r\n // going to a hybrid page and going back to a pure angular1 page\r\n if (count === 0) {\r\n return testCallback();\r\n }\r\n\r\n var decrement = function() {\r\n count--;\r\n if (count === 0) {\r\n testCallback();\r\n }\r\n };\r\n testabilities.forEach(function(testability) {\r\n testability.whenStable(decrement);\r\n });\r\n\r\n }\r\n else {testCallback();} // not an angular2 app\r\n };\r\n\r\n if (!(window.angular) && !(window.getAngularTestability)) {\r\n // no testability hook\r\n throw new Error(\r\n 'both angularJS testability and angular testability are undefined.' +\r\n ' This could be either ' +\r\n 'because this is a non-angular page or because your test involves ' +\r\n 'client-side navigation, which can interfere with Protractor\\'s ' +\r\n 'bootstrapping. See http://git.io/v4gXM for details');\r\n } else {waitForAngular1(waitForAngular2);} // Wait for angular1 and angular2\r\n // Testability hooks sequentially\r\n\r\n } catch (err) {\r\n callback(err.message);\r\n }\r\n\r\n","scriptTimeout":null,"specialPowers":false}]
1515700606228 Marionette TRACE 0 -> [0,96,"executeAsyncScript",{"args":[],"newSandbox":false,"script":"var callback = arguments[arguments.length - 1];\nvar rootSelector = 'null';\n\nvar getNg1Hooks = function(selector, injectorPlease) {\r\n function tryEl(el) {\r\n try {\r\n if (!injectorPlease && angular.getTestability) {\r\n var $$testability = angular.getTestability(el);\r\n if ($$testability) {\r\n return {$$testability: $$testability};\r\n }\r\n } else {\r\n var $injector = angular.element(el).injector();\r\n if ($injector) {\r\n return {$injector: $injector};\r\n }\r\n }\r\n } catch(err) {}\r\n }\r\n function trySelector(selector) {\r\n var els = document.querySelectorAll(selector);\r\n for (var i = 0; i < els.length; i++) {\r\n var elHooks = tryEl(els[i]);\r\n if (elHooks) {\r\n return elHooks;\r\n }\r\n }\r\n }\r\n\r\n if (selector) {\r\n return trySelector(selector);\r\n } else if (window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__) {\r\n var $injector = window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__;\r\n var $$testability = null;\r\n try {\r\n $$testability = $injector.get('$$testability');\r\n } catch (e) {}\r\n return {$injector: $injector, $$testability: $$testability};\r\n } else {\r\n return tryEl(document.body) ||\r\n trySelector('[ng-app]') || trySelector('[ng\\\\:app]') ||\r\n trySelector('[ng-controller]') || trySelector('[ng\\\\:controller]');\r\n }\r\n}\n\r\n\r\n try {\r\n // Wait for both angular1 testability and angular2 testability.\r\n\r\n var testCallback = callback;\r\n\r\n // Wait for angular1 testability first and run waitForAngular2 as a callback\r\n var waitForAngular1 = function(callback) {\r\n\r\n if (window.angular) {\r\n var hooks = getNg1Hooks(rootSelector);\r\n if (!hooks){\r\n callback(); // not an angular1 app\r\n }\r\n else{\r\n if (hooks.$$testability) {\r\n hooks.$$testability.whenStable(callback);\r\n } else if (hooks.$injector) {\r\n hooks.$injector.get('$browser')\r\n .notifyWhenNoOutstandingRequests(callback);\r\n } else if (!!rootSelector) {\r\n throw new Error(\r\n 'Could not automatically find injector on page: \"' +\r\n window.location.toString() + '\". Consider using config.rootEl');\r\n } else {\r\n throw new Error(\r\n 'root element (' + rootSelector + ') has no injector.' +\r\n ' this may mean it is not inside ng-app.');\r\n }\r\n }\r\n }\r\n else {callback();} // not an angular1 app\r\n };\r\n\r\n // Wait for Angular2 testability and then run test callback\r\n var waitForAngular2 = function() {\r\n if (window.getAngularTestability) {\r\n if (rootSelector) {\r\n var testability = null;\r\n var el = document.querySelector(rootSelector);\r\n try{\r\n testability = window.getAngularTestability(el);\r\n }\r\n catch(e){}\r\n if (testability) {\r\n return testability.whenStable(function() { testCallback(); });\r\n }\r\n }\r\n\r\n // Didn't specify root element or testability could not be found\r\n // by rootSelector. This may happen in a hybrid app, which could have\r\n // more than one root.\r\n var testabilities = window.getAllAngularTestabilities();\r\n var count = testabilities.length;\r\n\r\n // No angular2 testability, this happens when\r\n // going to a hybrid page and going back to a pure angular1 page\r\n if (count === 0) {\r\n return testCallback();\r\n }\r\n\r\n var decrement = function() {\r\n count--;\r\n if (count === 0) {\r\n testCallback();\r\n }\r\n };\r\n testabilities.forEach(function(testability) {\r\n testability.whenStable(decrement);\r\n });\r\n\r\n }\r\n else {testCallback();} // not an angular2 app\r\n };\r\n\r\n if (!(window.angular) && !(window.getAngularTestability)) {\r\n // no testability hook\r\n throw new Error(\r\n 'both angularJS testability and angular testability are undefined.' +\r\n ' This could be either ' +\r\n 'because this is a non-angular page or because your test involves ' +\r\n 'client-side navigation, which can interfere with Protractor\\'s ' +\r\n 'bootstrapping. See http://git.io/v4gXM for details');\r\n } else {waitForAngular1(waitForAngular2);} // Wait for angular1 and angular2\r\n // Testability hooks sequentially\r\n\r\n } catch (err) {\r\n callback(err.message);\r\n }\r\n\r\n","scriptTimeout":null,"specialPowers":false}]
1515700607013 Marionette TRACE 0 <- [1,96,null,{"value":null}]
1515700607013 geckodriver::marionette TRACE <- [1,96,null,{"value":null}]
1515700607013 webdriver::server DEBUG <- 200 OK {"value":null}
@andreastt Hello,
Thank you for your feedback. It was very helpful trying to isolate the problem using more basic examples, and I think that in fact it had nothing to do with geckodriver itself.
You then inject a long and complicated JavaScript that I don’t understand what does.
That javascript waits for angular testabilities to complete.
What happened (I think) was that things were going too fast. I was logging in and invoking a different url without waiting for route redirect to finish. I just added a wait for loading the #home route, and now everything works fine.
I am still not sure whether it is the way angular works, the way I made this specific example, or the way hash-based routes work in browsers.
As far as the update problem, I will try to rollback to 56.0.2, and if the problem persists, I will create a new bug thread.
This issue has been automatically locked since there has not been any recent activity after it was closed. If you have run into an issue you think is related, please open a new issue.
System
Testcase
First of all, what settings do I have to provide to geckodriver to disable auto-update of the firefox on startup? Since firefox 56.0.2 it would update on startup and fail all the tests because of that.
A website with 3 angular 2 routes : localhost/#/login, localhost/#/home, localhost/#/page Login func is on basic auth (if that matters), and pages contain some unique elements. Also, upon successful login, it redirects automatically to #home
The problem is: for no apparent reason, firefox would be stuck at #login route. I use SharedDriver from https://github.com/cucumber/cucumber-jvm/blob/master/examples/java-webbit-websockets-selenium/ to reuse the browser between the tests. The test goes as follows:
Repeat the same test 5 times.
What actually happens is after the first time, firefox would get stuck showing the home page on #/login route in the url box. Since I clear the cache, it should reload the website from scratch.
I should also mention that it did not happen before firefox 56.0.2.
Is it the way firefox processes routes that has changed? I have heard that hash-based routes are not great, and one should use html5 routes, but I do not have that capability yet.
Stacktrace
Trace-level log
https://gist.github.com/oiale/61cbb35e82ba8350a6ef46153f518472
(I am not sure how to use gist correctly, sorry)