To my understanding Protractor is meant to run on top of WebDriver on a Node.js server and send commands to a selenium server (feel free to correct me if I'm wrong).
Anyway, I was wondering if it is possible to load Protractor into a web page as a JavaScript library (like you would load jQuery for example) so it would be accessible from the JavaScript code in the page.
Can it be done? If so, how? What files do I need? What dependencies?
My goal is to use its capabilities of selecting elements by their various angular bindings, and its waiting for angular events capabilities.
I don't think you can. You can write your own locators, or use directives to isolate element/binding. Another good thing to do would be to dive into the protractor source code and see how they do it. Particularly check out their clientsidescripts.js file. Here is an example of how they find bindings. You would call it like this: findBindings('exampleBinding');
clientSideScripts.findBindings = function() {
var binding = arguments[0];
var using = arguments[1] || document;
var bindings = using.getElementsByClassName('ng-binding');
var matches = [];
for (var i = 0; i < bindings.length; ++i) {
var dataBinding = angular.element(bindings[i]).data('$binding');
if(dataBinding) {
var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding;
if (bindingName.indexOf(binding) != -1) {
matches.push(bindings[i]);
}
}
}
return matches; // Return the whole array for webdriver.findElements.
};
Related
I am trying to use the Client Libraries provided by Google to move traffic from one version of an app in AppEngine to another. However, the documentation for doing this just talks about using the rest API and not the client libraries.
Here is some example code:
var servicesClient = Google.Cloud.AppEngine.V1.ServicesClient.Create();
var updateServiceRequest = new UpdateServiceRequest();
updateServiceRequest.Name = "apps/myProject/services/myService";
var updateMask = new Google.Protobuf.WellKnownTypes.FieldMask();
updateServiceRequest.UpdateMask = updateMask;
// See below for what should go here...
var updateResponse = servicesClient.UpdateService(updateServiceRequest);
My question is what format do I use for the update mask?
According to the documentation I should put in:
split {"split": { "allocations": { "newVersion": 1 } } }
But when I try: updateMask.Paths.Add(#"split { ""split"": { ""allocations"": { ""myNewVersion"": 1 } } }");
... I get the exception:
"This operation is only supported on the following field(s): [labels, migration_config, network_settings, split, tag_to_target_map], but got field(s): [split { "split": { "allocations": { "myNewVersion": 1 } } }] from the update request.
Any ideas where I should put the details of the split in the field mask object? The property Paths just seems to be a collection of strings.
The examples for these libraries in Google's doco is pretty poor :-(
I raised a support ticket with Google and despite them suggesting a solution which didn't work exactly (due to trying to assign a string to the UpdateMask which needs a FieldMask object), I managed to use it to find the correct solution.
The code should be:
// appService is a previously retrieved Service object from the ListServices method
var updateServiceRequest = new UpdateServiceRequest();
updateServiceRequest.Name = appService.Name;
updateServiceRequest.UpdateMask = new Google.Protobuf.WellKnownTypes.FieldMask();
updateServiceRequest.UpdateMask.Paths.Add("split");
appService.Split.Allocations.Clear();
appService.Split.Allocations["newServiceVerison"] = 1;
updateServiceRequest.Service = appService;
In my project i use AngularJS so a directive for downloading files was created. It contains the following:
scope.$on('downloaded', function(event, data) {
var hiddenLink = document.createElement('a');
$(hiddenLink).attr({
href: 'data:application/tiff;base64,' + data.Attachment,
download: data.AttachmentFileName
});
if (isIEorFirefox) {
$(hiddenLink).click(function(event){
event.preventDefault();
var byteString = atob(data.Attachment);
var buffer = new ArrayBuffer(byteString.length);
var intArray = new Uint8Array(buffer);
for (var i = 0; i < byteString.length; i++) {
intArray[i] = byteString.charCodeAt(i);
}
var blob = new Blob([buffer],{type:'image/tiff'});
window.navigator.msSaveOrOpenBlob(blob, data.AttachmentFileName);
});
$(hiddenLink).trigger('click');
} else {
hiddenLink.click();
}
});
Previously there was an issue - download in IE simply didn't start - but for now as you can it has been eliminated. Though another issue remains - currently this code doesn't start download in Firefox. There is only one question - why?
UPDATE:
I've updated initial code because it didn't save file properly in IE. Now it does. Searching over the web i still cannot find a way to make file download in FF. Moreover FF still seems not to have any native way to save files according to this article https://hacks.mozilla.org/2012/07/why-no-filesystem-api-in-firefox/. I would be grateful if someone prove me wrong.
hiddenLink.click();
should perhaps be:
$(hiddenLink).click();
or same as other:
$(hiddenLink).trigger('click');
Assume you also need the event handler added as well...
I have a directive that powers an HTML5 Canvas visualization. This directive has a wide array of methods to modify different parts of the visualization. The issue is that multiple controllers that have a different parent/child/sibling relationship need to communicate to this directive. Right now I have it wired this pretty awful way of emitting events up to the parent controller of the directive and then broadcasting them to the directive.
I have heard of using a service to do something like this, but nothing really explain why. I thought of using something like this:
angular.service('CanvasCommunication', function($rootScope) {
this.canvasAction = function() { $rootScope.broadcast('canvasAction'); };
}
And then have listener in the canvas to actual execute that action. This service could be then injected into any controller that communicates with the canvas.
The issue with this is that $rootScope.broadcast() has terrible performance and I want to make sure this communication channel is built in the most efficient way.
Has anybody dealt with something like this and thought of something better?
I've had the same issue - controllers needing to interact with each other, different parts of the app sending messages to each other, etc. In my projects, I've implemented a MessageService. Here's a very basic version of one (but honestly more than sufficient):
module.factory('MessageService',
function() {
var MessageService = {};
var listeners = {};
var count = 0;
MessageService.registerListener = function(listener) {
listeners[count] = listener;
count++;
return (function(currentCount) {
return function() {
delete listeners[currentCount];
}
})(count);
}
MessageService.broadcastMessage = function(message) {
var keys = Object.keys(listeners);
for (var i = 0; i < keys.length; i++) {
listeners[keys[i]](message);
}
}
return MessageService;
}
);
You might want to have listeners registered for particular subjects, and filter messages by subject, or not. Mine also queue messages on subjects until they're cleared, so that the messages can be viewed when a new view loads (in order to pair for ex. 'Success - Saved file' with a page change).
I need to be able to load/unload angular applications dynamically without causing memory leaks. In jQuery you can do $("#elementHoldingMyWidget").remove(); and the proper destruction code gets executed, event handlers are unbound etc.
I've been unable to find anything in the angular docs mentioning the possibility of tearing down an app once it's been bootstrapped.
My 1st attempt was to destroy the rootScope like so:
var rootScope = $("body").scope();
rootScope.$destroy();
But this doesn't seem to be working, and I'm not sure how the injector and services would be cleaned up even if it did.
How should this be done?
Using AngularJS 1.4.0, $rootScope.$destroy() is working again (as it was broken in 1.2). Using this permits to switch between several angularJS apps:
var appManager = new function () {
this.currentAppName;
this.currentApp;
this.startApp = function (appContainerId, appName) {
if (this.currentApp) {
this.destroyApp(this.currentApp, this.currentAppName);
}
var appContainer = document.getElementById(appContainerId);
if (appContainer) {
this.currentAppName = appName;
this.currentApp = angular.bootstrap(appContainer, [appName]);
}
}
this.destroyApp = function (app, appName) {
var $rootScope = app.get('$rootScope');
$rootScope.$destroy();
}
}
// Call this when page is ready to rebootsrap app
appManager.startApp('divContainerId', 'app');
To tear down my application without presenting the user with a white page via $('body').empty, I first $delete() the child scopes and then remove all the properties from $rootScope:
/*
* Iterate through the child scopes and kill 'em
* all, because Angular 1.2 won't let us $destroy()
* the $rootScope
*/
var scope = $rootScope.$$childHead;
while (scope) {
var nextScope = scope.$$nextSibling;
scope.$destroy();
scope = nextScope;
}
/*
* Iterate the properties of the $rootScope and delete
* any that possibly were set by us but leave the
* Angular-internal properties and functions intact so we
* can re-use the application.
*/
for(var prop in $rootScope){
if (($rootScope[prop])
&& (prop.indexOf('$$') != 0)
&& (typeof($rootScope[prop]) === 'object')) {
$rootScope[prop] = null;
}
}
UPDATE March 10, 2013: I found that $('body').empty(); does not tear down the app. It still lives.
ORIGINAL POST:
Well, this post: https://github.com/angular/angular.js/issues/1537#issuecomment-10164971 claims that there is no 'official' app tear down (at the time of writing), but you can just empty the element holding the app like so:
$('body').empty();
If this is not what you are looking for you can go through these steps for a temporary solution to tearing your app down:
https://github.com/angular/angular.js/issues/1537#issuecomment-10184033
I am trying to get contents of http://www.yahoo.com using WebClient#DownloadStringAsync(). However as Silverlight doesn't allow cross domain calls i am getting TargetInvocationException. I know we have to put clientaccesspolicy.xml and crossdomain.xml in our web server root but that is possible only if i have control on my services. Currently Google is not under my control ;), so how do i handle it?
I've did a workaround by making a WCF service in my web application and then calling WebClient. This works perfectly but it is rather ineffecient. Is there any other better way than this?
Thanks in advance :)
Silverlight's cross domain restricitions cause many developers to implement workarounds. If you need to display the html page you get back you should look into Silverlight 4 (WebBrowser) control although this only seems to work when running out-of-browser mode.
If you need to parse through the content you can try some of the following:
For a managed code solution the proxy service you have already implemented is your best option.
Write a Java applet that returns this information. Silverlight can interopt to javascript which can interopt into Java applets. This also works in the reverse but a little difficult to setup. (If you need more info on this let me know).
Use javascript XmlHttpRequest to get the data you want from the source. This can be difficult when supporting multiple browsers. This link shows an example of how to do this (you will need to scroll down). Javascript get Html
Code:
var xmlHttpRequestHandler = new Object();
var requestObject;
xmlHttpRequestHandler.createXmlHttpRequest = function(){
var XmlHttpRequestObject;
if(typeof XMLHttpRequest != "undefined")
{
XmlHttpRequestObject = new XMLHttpRequest();
}
else if(window.ActiveXObject)
{
var tryPossibleVersions =["MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp","Microsoft.XMLHttp"];
for(i=0;i<tryPossibleVersions.length;i++)
{
try
{
XmlHttpRequestObject = new ActiveXObject(tryPossibleVersions[i]);
break;
}
catch(xmlHttpRequestObjectError)
{
// Ignore Exception
}
}
}
return XmlHttpRequestObject;}
function getHtml(){
var url = document.getElementById('url').value;
if(url.length > 0)
{
requestObject = xmlHttpRequestHandler.createXmlHttpRequest();
requestObject.onreadystatechange=onReadyStateChangeResponse;
requestObject.open("Get",url, true);
requestObject.send(null);
}}
function onReadyStateChangeResponse(){
var ready, status;
try
{
ready = requestObject.readyState;
status = requestObject.status;
}
catch(e) {}
if(ready == 4 && status == 200)
{
alert(requestObject.responseText);
}}