I'm trying to emulate a user story on my website with Protractor.
The user has to type in an input that uses auto-completion. In real life, the user has to type some text in the input, then select the right proposition with either her mouse or more naturally her downarrow key.
The problem is that I can't seem to simulate that with Protractor. element.sendKeys just does not allow you to do that. I have tried in a dozen different manners and it yields unpredictable results at best.
So I would like to manipulate the ng-model behing my input directly. Is there a way to access the scope of an element from Protractor and call functions/set properties on it?
Here is a simplified version of my problem :
View :
<div ng-controller="MyController">
<input id="my-input" ng-model="myModel"/>
</div>
Controller :
myModule.controller('MyController', ['$scope', function($scope){
$scope.myModel = "";
//[...]
}]);
e2e Protractor test :
describe("setting myModel to a fixture value", function(){
it("should set myModel to 'a test value'", function(){
var myInput = element('my-input');
// Now what?
});
});
You can use .evaluate() to set your model value directly:
var elm = element(by.model("obj.field"));
elm.evaluate("obj.field = 'test';");
To get the model value:
elm.evaluate("obj.field").then(function (value) {
console.log(value);
});
In this answer: How to select option in drop down protractorjs e2e tests
They use this: .sendKeys(protractor.Key.ARROW_DOWN); for sending DOWN arrows.
It worth a try.
Related
I am giving my first steps with Angular.js and I am facing a little problem which I don't know how to deal with Angular. I know many jQuery ways for solving it, but I am sure that it may exists an Angular way:
Firstly, I have an select element set with currentPlant as ng-model
<select class="form-control" name="planta" ng-model='currentPlant' ng-options='plant.name for plant in plants' >
</select>
I print the current name as page title, I see this element updating when I change the selected value, so I am sure that currentPlant is being correctly updated.
<h1>{{ currentPlant ? currentPlant.name : 'Seleccione una planta'}}</h1>
Now I have the following button:
<button class='btn btn-primary' ng-click='loadTowers()'>
Cargar torres
</button>
When the button is pressed, the loadTowers function is executed, and is now when the problems start. I have set the loadTowers function in the controller that way:
$scope.loadTowers = plants.towers;
plant is a angular service which has implemented the towers function inside. I need to get the value of currentPlant inside this function. Take a look:
.factory('plants', ['$http',function($http){
var o = {
plants: [],
towers : []
};
o.towers = function(){
var id = ¿?; // I NEED SET THIS VARIABLE WITH THE currentPlant.id VALUE
$http.get('/api/v1/plants/'+id+'/towers')
.success(function(data){
angular.copy(data, o.towers);
})
}
return o;
}]);
I could set an data-plant attribute in the option element, and get the selected through jQuery, but I am trying avoid this kind of solutions. How do you deal with this?
NOTE: Take account that I simply want get the option selected when I press the button and make different get requests to the API`in function of that through the service.
Thanks in advance.
Pass the value in the ngclick:
<button ng-click='loadTowers(currentPlant)'>
Then add it in the factory args:
o.towers = function(currentPlant){
}
Use Case
I am trying to set up the x-editable library around a knockout view model that can live inside of a js datatables instance. On load, it would pull down the tabular data. A user would be able to edit one field of one row and that would save automatically. I also adding some client side validation.
Problem
For my problem and corresponding simplified example, I took the dataTables portion out.
variable.subscribe inside the knockout viewmodel never seems to fire after x-editable changes it. I don't see any errors. I've check against multiple examples. Currently, I don't have any more clues to go off of.
Versions
knockout: 3.4.0
x-editable: 1.5.1
valib: 2.0.0
HTML
<div id="simple-view">
<span class="editable" data-bind="text: testA"></span>
</div>
JS - Knockout View Model Portion
(function(){
'use strict';
function SimpleViewModel(){
var self = this;
self.testA = ko.observable("Simple");
self.testA.subscribe(function(changes){
console.log("name changes: " + changes.length);
});
}
ko.applyBindings(new SimpleViewModel(), document.getElementById('simple-view'));
})();
JS - Call to X-Editable
document.addEventListener("DOMContentLoaded", function(event) {
$(".editable").editable({
type: 'text',
url: function(params){
console.log("url test: " + JSON.stringify(params));
},
mode: 'inline',
anim: false,
validate:function(value){
var result = valib.String.match(value, /^[a-zA-Z0-9-_]+$/);
console.log("inside validation: " + result);
if(!result){
return "This is a test validation response";
}
}
});
});
Problem Recap
I can see console logs for firing the validation message and for the url parameter (which here is practically an on submit for x-editable). However, I have never seen the subscribe function work. In my non canned example code, I was also working with observable arrays. Didn't work there. Does subscribe simply not pick up changes from the browser side but instead only when the view model changes (e.g. when some code pushes data onto an observable array?)
P.S.
I was originally trying to get knockout-x-editable to work with knockout-validation. However, k-x-e never worked after trying every example I could find and stepping through the plugin code. Something was expected from k-x-e for the ko-validation plugin to work but it wasn't firing an error message. As such, I eventually hit a brick wall I could get past and decided to try the above approach.
You need x-editable custom binding to enable x-editable play nice with knockoutjs.
X-editable custom binding for knockoutjs is available here. Include this binding after knockoutjs in your page.
So you will do
<div id="simple-view">
<span class="editable" data-bind="editable: testA, editableOptions:{/* your x-editable options here type, url, mode etc */}"> </span>
</div>
and your JS - Knockout View Model Portion
(function(){
'use strict';
function SimpleViewModel(){
var self = this;
self.testA = ko.observable("Simple");
self.testA.subscribe(function(changes){
console.log("name changes: " + changes.length);
});
}
ko.applyBindings(new SimpleViewModel(), document.getElementById('simple-view'));
})();
and then your xeditable controls will be in-sync with your observables.
I'm looking to write some tests that test the wiring between Backbone views and their models. I essentially want to load up my view with a model and make sure everything is peachy (events properly triggered/handled, elements added to the DOM, etc). I see this as different from acceptance/functional/e2e testing, but larger than simple unit tests. In other words, I'm not looking to write:
var browser = new Browser()
, fakeData = readFixtures("persons.json");
fakeAPIResponse('/persons', fakeData);
browser.visit("http://localhost:3000/", function () {
assert.ok(browser.success);
var persons = browser.queryAll(".persons li");
assert.lengthOf(persons, 20);
});
but rather something like
var router = require('routers/main'),
UserModel = require('models/user'),
AccountView = require('views/account');
...
# In a test
var model = new UserModel({ userId: 1 });
router._showView(new AccountView({ model: model });
expect(document.getElementsByClassName('account-panel')).to.have.length(1);
model.set('name', 'Test Testerson');
expect(document.getElementById('name-field').value).to.equal('Test Testerson');
Maybe I'm way off and should just put together some end-to-end tests but this seems to me like it'd be a valuable way of testing. My question is: how can I accomplish this? I need a full DOM, so I'm thinking that these should run in something like PhantomJS; the DOM should be reset before each test, but it seems silly/inefficient to have the browser navigate to a new page for each individual test. Is there a framework out there for running a test this way? Feel free to tell me I'm wrong for wanting this.
We are doing this using casperJs. Casper will give you the full DOM, but this should not be the reason the choose integration test over unit test. When I find myself going way out of my way to create a test environment, I tend to choose integration.
However, having said that, I think you can get away with doing a unit test and here is how I would do it. We are using testem with chaiJs assertions for such tests. If you setup a fixture, you can do something like this (and you'll have the full DOM, and ability to test events and everything else listed in your OP).
beforeEach(function () {
var theModel = new Backbone.Model(),
theViewToTest = new TheViewToTest({
model: theModel,
el: $(fixtures.get('some-fixture.html'))
});
theViewToTest.render();
this.theViewToTest = theViewToTest;
});
describe('Checking event handlers', function () {
it('Should fire some events', function () {
// I would setup a spy here and listen to the button handler
this.theViewToTest.$el.find('.some-button').trigger('click');
// Assert the spy was called once
});
});
I have created a datatable showing a list of persons and their details. When the datatable is clicked, it has to show the entity of the single person with their details but my problem is, when I click the datatable it is opening a chat box showing the entity of the last clicked person changing all other chat box details.
1.How can I limit the append directive, that is, one chatbox for one id?
2.How to display the name of the particular person without changing the older chat box entity?
Here is the link to Plunker http://plnkr.co/edit/VTWcZQjlAda0KQ9sjFzI?p=preview
Actually i really think there is no need for a directive here.
It can simply be done by using ng-repeat and a collection.
See it working in this plunker
I added this in the controller :
$scope.bottomListCollection = [];
$scope.addToBottomList = function(artist) {
$scope.bottomListCollection.push(artist);
}
And this kind of ng-click on your rows :
ng-click="addToBottomList(item)"
Some advices to do things cleaner in angular :
Never use :
$compile.
$element.
$broadcast.
Jquery.
Take care of custom directives, theses are 90% miss-used.
Just a reminder : Directives are intended to add a behavior on an element. Not adding html.
Hope it helped.
Some tips that fix your issue
childScope = $scope.$new(true);
to ve an isolated scope you ve to use the first parameter of $new method
for more info look at the docs ($new(isolate, parent); --> https://docs.angularjs.org/api/ng/type/$rootScope.Scope)
then you need to add a control on the AppendText function like this to check if the same chat already exist
$scope.AppendText = function(idx) {
$scope.userInfo = $scope.artists[idx];
var chat = $('#chat_'+$scope.userInfo.shortname);
console.log(chat);
if ($scope.stage === 'Add' && chat.length==0 ) {
childScope = $scope.$new(true);
childScope.userInfo = $scope.userInfo; //<--- add old scope info to new scope
var compiledDirective = $compile('<div my-directive></div>');
var directiveElement = compiledDirective(childScope);
$('.my-directive-placeholder').append(directiveElement);
} else {
$scope.stage = 'Add';
}
}
working plunker http://plnkr.co/edit/TFjlN0U2i4irKtG2D5yu?p=preview
To answer the second part of your question : Create an isolate scope!
That can be done by passing true while creating a new scope: childScope = $scope.$new(true).
Once the isolate scope is created, you can do:
childScope.userInfo = $scope.userInfo;
PLUNK : http://plnkr.co/edit/IxPh4EmLpr8WAqRWtRlo?p=preview
Also, a hackish solution using one time databinding (not recommended):
http://plnkr.co/edit/RjZNOSyaemqg2eZ4Gma1?p=preview
To answer the first part: You could keep track of the id's that are passed to the $scope.AppendText function perhaps?
PLUNK: http://plnkr.co/edit/BCNju0rToyVYvNjqzVON?p=preview
Hope this helps! IMHO it would be much more simpler if you could just ng-repeat over your json data to generate the chatboxes.
I am integrating a payment provider into a ExtJS websites.
Basically, a form needs to be created and the form fields is send to the payment provider using Ajax.
The problem is that the payment provider does not allow that the form fields has a "name" param assigned to the "" tag. They do a manual check of the implementation and makes sure it is not there.
I assume it is a counter-mesasure for when the visitor has Ajax dissabled and the form gets submitted to my server instead, revealing the credit card. I know it does not make any sense with ExtJS, as it would not work without Javascript turned on, but non-the-less, that is the rule from the payment provider.
So, how can I force ExtJS to not put a "name" param in the form field? I have tried putting "name: ''" into the fields, but that gets ignored.
Do I use the template-system in ExtJS to solve this?
So Eric is perfectly right that it can be done much easier then modifying the whole template but non the less I would use a plugin for such a special case. I made a quick one:
Ext.define('Ext.form.field.plugin.NoNameAttribute', {
extend: 'Ext.AbstractPlugin',
alias: 'plugin.nonameattribute',
init: function(cmp) {
Ext.Function.interceptAfterCust(cmp, "getSubTplData", function(data){
delete data['name'];
return data;
});
}
});
Note the used method interceptAfterCust is a custom one of mine that modify the existing one by handing the result of the original to the intercepting one as argument. It is also using the given original object (which can be threaten as a scope) as scope for the original method. The easiest would be to add these method to Ext.Function
Ext.Function.interceptAfterCust = function(object, methodName, fn, scope) {
var method = object[methodName] || Ext.emptyFn;
return (object[methodName] = function() {
return fn.call(scope || this, method.apply(object, arguments));
});
}
Here is a working JSFiddle where the first field will not have a name attribute on the dom even if it exist in the component.
There's a surprisingly simple solution to this. I tested it with Ext.form.field.Text and Ext.form.field.ComboBox and it works well, but I don't know if it works for all form fields or if there are any negative side-effects. Use with caution.
Ext.define('Override.form.field.Base', {
override: 'Ext.form.field.Base',
getSubTplData: function(){
var data = this.callParent(arguments);
delete data.name;
return data;
}
});
Basically, it removes the auto-generated name from the render data before passing it along. The best part is that no private methods are involved so this should be a stable solution.
I prefer this in the field config options:
submitValue: false
Available since ExtJS 3.4