Reusable code for multiple describe() in the same spec file - protractor

Since I need to have different beforeAll() and beforeEach() implementations for different tests within the same spec file, I have decided to have two describe() within the spec file. However, there are some methods originally implemented within the first describe(), which they will also be used in the second describe(). I would like to know if it is possible to put those methods out of the describe() scope and place them in the outermost scope of the spec file. I have tested this approach and it is working fine without any errors. However, I would like to know if this approach follows code style standard?
Here is an example. Supposedly I have a spec file example.spec.ts
describe("Example", function() {
// Declare variables
beforeAll(function() {
// Implementation of beforeAll()
// ...
// ...
});
beforeEach(function() {
// Implementation of beforeEach()
// ...
// ...
});
it("test one", function() {
// Implementation of test one
// ...
helperMethod()
// ...
});
helperMethod() {
// Implementation of helperMethod()
// ...
// ...
});
});
Now that I have another describe() inside the spec file and the helper method is shared between the two describe():
describe("Example1", function() {
// Declare variables
beforeAll(function() {
// Implementation of beforeAll()
// ...
// ...
});
beforeEach(function() {
// Implementation of beforeEach()
// ...
// ...
});
it("test one", function() {
// Implementation of test one
// ...
helperMethod()
// ...
});
});
describe("Example2", function() {
// Declare variables
beforeAll(function() {
// Implementation of beforeAll()
// ...
// ...
});
beforeEach(function() {
// Implementation of beforeEach()
// ...
// ...
});
it("test two", function() {
// Implementation of test one
// ...
helperMethod()
// ...
});
});
helperMethod() {
// Implementation of helperMethod()
// ...
// ...
});
I have tested this approach and no errors are returned. The tests are performed successfully. However, I am not sure if this follows the code style standard. Is this a good approach to resolve the problem? Or do I have to instead place the helperMethod() into a page object file?

Related

How to Register custom handelbars helper in assemble 0.17.1

In my assemblefile.js I try to register a custom helper. The helper itself does work since i have it in use in a grunt project with assemble.
assemble: {
options: {
helpers: ['./src/helper/custom-helper.js' ]
}
}
In assemble 0.17.1 I tried it like this but it doesn´t work. Does anyone know how to do this?
app.helpers('./src/helper/custom-helper.js');
custom-helper.js:
module.exports.register = function (Handlebars, options, params) {
Handlebars.registerHelper('section', function(name, options) {
if (!this.sections) {
this.sections = {};
}
this.sections[name] = options.fn(this);
return null;;
});
};
assemble is built on top of the templates module now, so you can use the .helper and .helpers methods for registering helpers with assemble, which will register them with Handlebars. This link has more information on registering the helpers.
Since the templates api is used, you don't have to wrap the helpers with the .register method in your example. You can just export the helper function, then name it when registering with assemble like this:
// custom-helper.js
module.exports = function(name, options) {
if (!this.sections) {
this.sections = {};
}
this.sections[name] = options.fn(this);
return null;
};
// register with assemble
var app = assemble();
app.helper('section', require('./custom-helper.js'));
You may also export an object with helpers and register them all at once using the .helpers method:
// my-helpers.js
module.exports = {
foo: function(str) { return 'FOO: ' + str; },
bar: function(str) { return 'BAR: ' + str; },
baz: function(str) { return 'BAZ: ' + str; }
};
// register with assemble
var app = assemble();
app.helpers(require('./my-helpers.js'));
When registering the object with the .helpers method, the property keys are used for the helper names

Extend CRUD functions

I want to add some functionality to the built-in create function.
I have a model called user, and I know I can override the create function by declaring my own create function in the UserController:
module.exports = {
create: function(req, res) {
// Logic here for checking if everything is okay
if (everything_okay) {
// call super.create() ? <--
}
else{
res.josn({ error: err });
}
},
};
I read through the docs, but couldn't find out how to implement the indicated <-- line.
// call super.create() ? <-- means this is the place where you call:
User.create(userObject, function (err, cretaedUserObj) {
if(err) {
//handle err
} else {
//user succesfully created.
}
});
So basically, if all the parameters are OK i.e. if(everything_okay), you create the User, else you handle that which is Not Ok.
Hope this helps.

Call controller on afterCreate

I have the following code for my Sessions model:
module.exports = {
attributes: {
},
afterCreate: function(value,next) {
next();
}
};
And the following Sessions Controller:
module.exports = {
saveSession: function(res,req) {
console.log('in save');
}
};
I want to save a value to a user's session afterCreate
How can I call the saveSession function from my model? I tried Sessions.saveSession() but it doesn't work.
I don't think you need a session model and it's not a good idea to call a controller method directly.
I'd recommend just set req.session when you're trying to save the session and it'll be auto-saved when you respond from that controller action.
afterCreate will never have access to req unless you pass it down which I wouldn't recommend.
The pattern is something like:
{
// …
login: function (req,res) {
User.findOne({
username: req.param('username'),
password: req.param('password')
}).exec(function (err, user) {
if (err) return res.serverError(err);
if (!user) return res.view('/login');
req.session.user = user.toJSON();
return res.redirect('/dashboard');
});
}
// ...
I think that you want to save a value to a cookie or create another database record am i correct?
If so, you dont need to call a controller action from the model (not recommended), you just need to create a new record or save the value to the cookie, here are some alternatives that i see possible in your scenario.
creating another record:
// on models/YourModel
module.exports = {
attributes: {
},
afterCreate: function(newlyInsertedRecord,next) {
ModelOrResource.create({
param1: newlyInsertedRecord.attributeYouWant,
param2: value2
// and so on
}).exec(function(err, recordCreated){
if(err) return next(err);
// do somethign with recordCreated if you need to
// ...
next();
})
}
};
Saving a value to a cookie:
module.exports = {
attributes: {
},
afterCreate: function(newlyInsertedRecord, next) {
// do some other stuff not related to calling a controller action ;)
next();
}
};
This code was retrived from snippets from my own projects, so it should work on sails 0.9.x
Hope it helps!

Non-Singleton Services in AngularJS

AngularJS clearly states in its documentation that Services are Singletons:
AngularJS services are singletons
Counterintuitively, module.factory also returns a Singleton instance.
Given that there are plenty of use-cases for non-singleton services, what is the best way to implement the factory method to return instances of a Service, so that each time an ExampleService dependency is declared, it is satisfied by a different instance of ExampleService?
I'm not entirely sure what use case you are trying to satisfy. But it is possible to have a factory return instances of an object. You should be able to modify this to suit your needs.
var ExampleApplication = angular.module('ExampleApplication', []);
ExampleApplication.factory('InstancedService', function(){
function Instance(name, type){
this.name = name;
this.type = type;
}
return {
Instance: Instance
}
});
ExampleApplication.controller('InstanceController', function($scope, InstancedService){
var instanceA = new InstancedService.Instance('A','string'),
instanceB = new InstancedService.Instance('B','object');
console.log(angular.equals(instanceA, instanceB));
});
JsFiddle
Updated
Consider the following request for non-singleton services. In which Brian Ford notes:
The idea that all services are singletons does not stop you from
writing singleton factories that can instantiate new objects.
and his example of returning instances from factories:
myApp.factory('myService', function () {
var MyThing = function () {};
MyThing.prototype.foo = function () {};
return {
getInstance: function () {
return new MyThing();
}
};
});
I would also argue his example is superior due to the fact that you do not have to use the new keyword in your controller. It is encapsulated within the getInstance method of the service.
I don't think we should ever have a factory return a newable function as this begins to break down dependency injection and the library will behave awkwardly, especially for third parties. In short, I am not sure there are any legitimate use cases for non-singleton sevices.
A better way to accomplish the same thing is to use the factory as an API to return a collection of objects with getter and setter methods attached to them. Here is some pseudo-code showing how using that kind of service might work:
.controller( 'MainCtrl', function ( $scope, widgetService ) {
$scope.onSearchFormSubmission = function () {
widgetService.findById( $scope.searchById ).then(function ( widget ) {
// this is a returned object, complete with all the getter/setters
$scope.widget = widget;
});
};
$scope.onWidgetSave = function () {
// this method persists the widget object
$scope.widget.$save();
};
});
This is just pseudo-code for looking up a widget by ID and then being able to save changes made to the record.
Here's some pseudo-code for the service:
.factory( 'widgetService', function ( $http ) {
function Widget( json ) {
angular.extend( this, json );
}
Widget.prototype = {
$save: function () {
// TODO: strip irrelevant fields
var scrubbedObject = //...
return $http.put( '/widgets/'+this.id, scrubbedObject );
}
};
function getWidgetById ( id ) {
return $http( '/widgets/'+id ).then(function ( json ) {
return new Widget( json );
});
}
// the public widget API
return {
// ...
findById: getWidgetById
// ...
};
});
Though not included in this example, these kinds of flexible services could also easily manage state.
I don't have time right now, but if it will be helpful I can put together a simple Plunker later to demonstrate.
Another way is to copy service object with angular.extend().
app.factory('Person', function(){
return {
greet: function() { return "Hello, I'm " + this.name; },
copy: function(name) { return angular.extend({name: name}, this); }
};
});
and then, for example, in your controller
app.controller('MainCtrl', function ($scope, Person) {
michael = Person.copy('Michael');
peter = Person.copy('Peter');
michael.greet(); // Hello I'm Michael
peter.greet(); // Hello I'm Peter
});
Here is a plunk.
I know this post has already been answered but I still think there would be some legitimate scenarios that you need to have non-singleton service. Let's say there are some reusable business logic which can be shared between several controllers. In this scenario the best place to put the logic would be a service, but what if we need to keep some state in our reusable logic? Then we need non-singleton service so can be shared across different controllers in app. This is how I would implement these services:
angular.module('app', [])
.factory('nonSingletonService', function(){
var instance = function (name, type){
this.name = name;
this.type = type;
return this;
}
return instance;
})
.controller('myController', ['$scope', 'nonSingletonService', function($scope, nonSingletonService){
var instanceA = new nonSingletonService('A','string');
var instanceB = new nonSingletonService('B','object');
console.log(angular.equals(instanceA, instanceB));
}]);
Here's my example of a non singleton service, It's from a ORM im working on. In the example I show a Base Model (ModelFactory) which I want services('users','documents') to inherit and potential extend.
In my ORM ModelFactory injects other services to provide extra functionality(query,persistence,schema mapping) which is sandboxed using the module system.
In the example both user and document service have the same functionality but have their own independent scopes.
/*
A class which which we want to have multiple instances of,
it has two attrs schema, and classname
*/
var ModelFactory;
ModelFactory = function($injector) {
this.schema = {};
this.className = "";
};
Model.prototype.klass = function() {
return {
className: this.className,
schema: this.schema
};
};
Model.prototype.register = function(className, schema) {
this.className = className;
this.schema = schema;
};
angular.module('model', []).factory('ModelFactory', [
'$injector', function($injector) {
return function() {
return $injector.instantiate(ModelFactory);
};
}
]);
/*
Creating multiple instances of ModelFactory
*/
angular.module('models', []).service('userService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("User", {
name: 'String',
username: 'String',
password: 'String',
email: 'String'
});
return instance;
}
]).service('documentService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("Document", {
name: 'String',
format: 'String',
fileSize: 'String'
});
return instance;
}
]);
/*
Example Usage
*/
angular.module('controllers', []).controller('exampleController', [
'$scope', 'userService', 'documentService', function($scope, userService, documentService) {
userService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
username : 'String'
password: 'String'
email: 'String'
}
}
*/
return documentService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
format : 'String'
formatileSize: 'String'
}
}
*/
}
]);
angular only gives a singleton service/factory option.
one way around it is to have a factory service that will build a new instance for you inside your controller or other consumer instances.
the only thing that is injected is the class that creates new instances.
this is a good place to inject other dependencies or to initialize your new object to the specification of the user (adding services or config)
namespace admin.factories {
'use strict';
export interface IModelFactory {
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel;
}
class ModelFactory implements IModelFactory {
// any injection of services can happen here on the factory constructor...
// I didnt implement a constructor but you can have it contain a $log for example and save the injection from the build funtion.
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel {
return new Model($log, connection, collection, service);
}
}
export interface IModel {
// query(connection: string, collection: string): ng.IPromise<any>;
}
class Model implements IModel {
constructor(
private $log: ng.ILogService,
private connection: string,
private collection: string,
service: admin.services.ICollectionService) {
};
}
angular.module('admin')
.service('admin.services.ModelFactory', ModelFactory);
}
then in your consumer instance you need the factory service and call the build method on the factory to get a new instance when you need it
class CollectionController {
public model: admin.factories.IModel;
static $inject = ['$log', '$routeParams', 'admin.services.Collection', 'admin.services.ModelFactory'];
constructor(
private $log: ng.ILogService,
$routeParams: ICollectionParams,
private service: admin.services.ICollectionService,
factory: admin.factories.IModelFactory) {
this.connection = $routeParams.connection;
this.collection = $routeParams.collection;
this.model = factory.build(this.$log, this.connection, this.collection, this.service);
}
}
you can see it provides opperatiunity to inject some specific services that are not available in the factory step.
you can always have injection happen on the factory instance to be used by all Model instances.
Note I had to strip off some code so I might made some context errors...
if you need a code sample that works let me know.
I believe that NG2 will have the option to inject a new instance of your service in the right place in your DOM so you dont need to build your own factory implementation. will have to wait and see :)
I believe there is good reason to create a new instance of an object within a service. We should keep an open mind as well rather than just say we ought never do such a thing, but the singleton was made that way for a reason. Controllers are created and destroyed often within the lifecycle of the app, but the services must be persistent.
I can think of a use case where you have a work flow of some kind, like accepting a payment and you have multiple properties set, but must now change their payment type because the customer's credit card failed and they need to provide a different form of payment. Of course, this does have a lot to do with the way you create your app. You could reset all properties for the payment object, or you could create a new instance of an object within the service. But, you would not want a new instance of the service, nor would you want to refresh the page.
I believe a solution is providing an object within the service that you can create a new instance of and set. But, just to be clear, the single instance of the service is important because a controller may be created and destroyed many times, but the services need persistence. What you are looking for may not be a direct method within Angular, but an object pattern that you can manage inside your service.
As an example, I have a made a reset button. (This is not tested, its really just a quick idea of a use case for creating a new object within a service.
app.controller("PaymentController", ['$scope','PaymentService',function($scope, PaymentService) {
$scope.utility = {
reset: PaymentService.payment.reset()
};
}]);
app.factory("PaymentService", ['$http', function ($http) {
var paymentURL = "https://www.paymentserviceprovider.com/servicename/token/"
function PaymentObject(){
// this.user = new User();
/** Credit Card*/
// this.paymentMethod = "";
//...
}
var payment = {
options: ["Cash", "Check", "Existing Credit Card", "New Credit Card"],
paymentMethod: new PaymentObject(),
getService: function(success, fail){
var request = $http({
method: "get",
url: paymentURL
}
);
return ( request.then(success, fail) );
}
//...
}
return {
payment: {
reset: function(){
payment.paymentMethod = new PaymentObject();
},
request: function(success, fail){
return payment.getService(success, fail)
}
}
}
}]);
Here's another approach to the problem that I was quite satisfied with, specifically when used in combination with Closure Compiler with advanced optimizations enabled:
var MyFactory = function(arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
};
MyFactory.prototype.foo = function() {
console.log(this.arg1, this.arg2);
// You have static access to other injected services/factories.
console.log(MyFactory.OtherService1.foo());
console.log(MyFactory.OtherService2.foo());
};
MyFactory.factory = function(OtherService1, OtherService2) {
MyFactory.OtherService1_ = OtherService1;
MyFactory.OtherService2_ = OtherService2;
return MyFactory;
};
MyFactory.create = function(arg1, arg2) {
return new MyFactory(arg1, arg2);
};
// Using MyFactory.
MyCtrl = function(MyFactory) {
var instance = MyFactory.create('bar1', 'bar2');
instance.foo();
// Outputs "bar1", "bar2" to console, plus whatever static services do.
};
angular.module('app', [])
.factory('MyFactory', MyFactory)
.controller('MyCtrl', MyCtrl);

call parent class function on extjs 3

i have extjs class like this for Add:
Ext.ns('Example');
Example.Form = Ext.extend(Ext.form.FormPanel, {
,initComponent:function() {
// hard coded - cannot be changed from outsid
var config = {
items: [{
xtype: 'textfield',
fieldLabel: 'title',
name: 'author',
allowBlank: false
}
.........................
]
,buttons:[
{
text:'submit'
,formBind:true
,scope:this
,handler:this.submit
}]
}; // eo config object
// apply config
Ext.apply(this, Ext.apply(this.initialConfig, config));
// call parent
Example.Form.superclass.initComponent.apply(this, arguments);
} // eo function initComponent
/**
* Form onRender override
*/
,onRender:function() {
..................
} // eo function onRender
/**
* Reset
*/
,reset:function() {
this.getForm().reset();
} // eo function onRender
/**
* Load button click handler
*/
,onLoadClick:function() {
....................
}
,submit:function() {
........................
}
,onSuccess:function(form, action) {
..........................
}
,onFailure:function(form, action) {
......................
} // eo function onFailure
,showError:function(msg, title) {
........................
});
}
});
and i have another extend for Edit:
Example.Form2 = Ext.extend(Example.Form, {
......
});
how i can call "onLoadClick" function from first class in secound class?because i want to load data to my form before form load.
If you have one class that extends another class you can call the "parent" class methods by using the superclass property on your class definition.
In the example below we add a special function mySpecial add and make it call the parent classes add function.
Ext.ux.myForm = Ext.extend(Ext.form.FormPanel, {
...
mySpecialAdd: function(comp, anExtraParam) {
// some special handling here
...
// call parent class add
return Ext.ux.myForm.superclass.add.call(this, comp);
}
})
instead of call you can also choose to use apply
Ext.ux.myForm = Ext.extend(Ext.form.FormPanel, {
...
mySpecialAdd: function(comp, anExtraParam) {
// some special handling here
...
// call parent class add
return Ext.ux.myForm.superclass.add.apply(this, [comp]);
}
})
Do note that "this" will still be your new class, so any other function that you overwrite will be the one called from the parent class and not the parent class function.
Example
Ext.form.FormPanel have an onAdd method that is being called in Ext.form.FormPanel.add, so if you overwrite onAdd then its your function that is called.
Ext.ux.myForm = Ext.extend(Ext.form.FormPanel, {
...
mySpecialAdd: function(comp, anExtraParam) {
// some special handling here
...
// call parent class add
return Ext.ux.myForm.superclass.add.apply(this, [comp]);
},
// even though onAdd is marked as private you are actually still overwriting it here
// because of the way its implemented in Ext 3
onAdd: function(c) {
// this will get called from the parent class (Ext.form.FormPanel) add method.
...
// so make sure you handle it nicely
Ext.ux.myForm.superclass.onAdd.call(this, c);
...
}
})
If you need to call a superclass method into a method of the subclass, you can do something like this:
Example.Form2 = Ext.extend(Example.Form, {
testMethod: function(){
...
//call parent class method
Example.Form2.superclass.doLoadClick.call(this);
...
}
});
Is that what you mean ?