Ember.js deprecation of registerImplementation in favour of App.initializer - javascript

I am using an extension of HashLocation to implement a hashbang url type for Ember.js.
Here is the code snippet:
(function() {
var get = Ember.get, set = Ember.set;
Ember.Location.registerImplementation('hashbang', Ember.HashLocation.extend({
getURL: function() {
return get(this, 'location').hash.substr(2);
},
setURL: function(path) {
get(this, 'location').hash = "!"+path;
set(this, 'lastSetURL', "!"+path);
},
onUpdateURL: function(callback) {
var self = this;
var guid = Ember.guidFor(this);
Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
Ember.run(function() {
var path = location.hash.substr(2);
if (get(self, 'lastSetURL') === path) { return; }
set(self, 'lastSetURL', null);
callback(location.hash.substr(2));
});
});
},
formatURL: function(url) {
return '#!'+url;
}
}));
})();
I use this by reopening the Router:
App.Router.reopen({
location: 'hashbang'
});
However, on running the application, i'm hitting the following deprecation:
DEPRECATION: Using the Ember.Location.registerImplementation is no longer supported. Register your custom location implementation with the container instead.
I can't find any information on how to do this. Does anyone have any implementation snippets on what I would have to do?

According to deprecation message. use container instead.
(function() {
var get = Ember.get, set = Ember.set;
var hashbangLocation = Ember.HashLocation.extend({
getURL: function() {
return get(this, 'location').hash.substr(2);
},
setURL: function(path) {
get(this, 'location').hash = "!"+path;
set(this, 'lastSetURL', "!"+path);
},
onUpdateURL: function(callback) {
var self = this;
var guid = Ember.guidFor(this);
Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
Ember.run(function() {
var path = location.hash.substr(2);
if (get(self, 'lastSetURL') === path) { return; }
set(self, 'lastSetURL', null);
callback(location.hash.substr(2));
});
});
},
formatURL: function(url) {
return '#!'+url;
}
});
App.register('location:hashbang', hashbangLocation);
})();
Reopen it as usual
App.Router.reopen({
location: 'hashbang'
});

Related

Set browserName with protractor test suit name

I am trying to set the browser name with protractor test suite name. Because I am using allure reporter for test report. But the problem is I am getting browser name 'undefined' all the time. So far I have implemented browser name in an asynchronous method and I would like to use that method in suitstarted. the suitstarted method is synchronous. I cannot set suit started to asynchronous. In that way how can I wait suitstarted method and get the browser name after the suit.fullname. I am really stuck with this issue from last three days hence I am very new in Javascript.
Here is my code
require('protractor/built/logger').Logger.logLevel = 3;
exports.config = {
sync: false,
multiCapabilities:[
{
'browserName' : 'chrome',
'chromeOptions': { 'args' : ['--disable-extensions']},
'shardTestFiles': true,
'maxInstances': 1,
'unexpectedAlertBehaviour' : 'accept'
},
{
'browserName' : 'firefox',
},
{
'browserName': 'internet explorer',
'se:ieOptions': {
ignoreProtectedModeSettings: true,
'ie.ensureCleanSession': true,
}
},
],
jvmArgs: ['-Dwebdriver.ie.driver=./node_modules/webdriver-manager/selenium/IEDriverServer3.141.0.exe'],
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
isVerbose: true,
showColors: true,
defaultTimeoutInterval: 1500000
},
useAllAngular2AppRoots: true,
beforeLaunch: function() {
},
onPrepare: function() {
// ------------------------------------------------------
var Allure = require('allure-js-commons');
var path = require('path');
var allure = new Allure();
var AllureReporter = function CustomJasmine2AllureReporter(userDefinedConfig, allureReporter) {
var Status = {PASSED: 'passed', FAILED: 'failed', BROKEN: 'broken', PENDING: 'pending'};
this.allure = allureReporter || allure;
this.configure = function(userDefinedConfig) {
var pluginConfig = {};
userDefinedConfig = userDefinedConfig || {};
pluginConfig.resultsDir = userDefinedConfig.resultsDir || 'allure-results';
pluginConfig.basePath = userDefinedConfig.basePath || '.';
var outDir = path.resolve(pluginConfig.basePath, pluginConfig.resultsDir);
this.allure.setOptions({targetDir: outDir});
};
this.configure(userDefinedConfig);
var browserNameforSuit;
Edited
this.suiteStarted = function(suite) {
var capsPromise = browser.getCapabilities();
capsPromise.then(function (caps) {
browserNameforSuit = caps.get('browserName');
console.log(browserNameforSuit)
this.allure.startSuite(suite.fullName);
console.log(suite.fullName);
})
};
};
this.suiteDone = function() {
this.allure.endSuite();
};
this.specStarted = function(spec) {
this.allure.startCase(spec.description);
};
this.specDone = function(spec) {
var status = this._getTestcaseStatus(spec.status);
var errorInfo = this._getTestcaseError(spec);
this.allure.endCase(status, errorInfo);
};
this._getTestcaseStatus = function(status) {
if (status === 'disabled' || status === 'pending') {
return Status.PENDING;
} else if (status === 'passed') {
return Status.PASSED;
} else {
return Status.FAILED;
}
};
this._getTestcaseError = function(result) {
if (result.status === 'disabled') {
return {
message: 'This test was ignored',
stack: ''
};
} else if (result.status === 'pending') {
return {
message: result.pendingReason,
stack: ''
};
}
var failure = result.failedExpectations ? result.failedExpectations[0] : null;
if (failure) {
return {
message: failure.message,
stack: failure.stack
};
}
};
}
// ------------------------------------------------------
var Runtime = require('allure-js-commons/runtime');
let gallure = new Runtime(allure);
browser.manage().window().maximize();
require('ts-node').register({
project: 'e2e/tsconfig.json'
});
jasmine.getEnv().addReporter(new AllureReporter ({
resultsDir: 'allure-results'
}));
jasmine.getEnv().addReporter(reporter);
jasmine.getEnv().afterEach(function (done) {
browser.takeScreenshot().then(function (png) {
gallure.createAttachment('Screenshot', function () {
return new Buffer(png, 'base64')
},'image/png')();
done();
})
});
}
};
The index.js for Allure is
'use strict';
var assign = require('object-assign'),
Suite = require('./beans/suite'),
Test = require('./beans/test'),
Step = require('./beans/step'),
Attachment = require('./beans/attachment'),
util = require('./util'),
writer = require('./writer');
function Allure() {
this.suites = [];
this.options = {
targetDir: 'allure-results'
};
}
Allure.prototype.setOptions = function(options) {
assign(this.options, options);
};
Allure.prototype.getCurrentSuite = function() {
return this.suites[0];
};
Allure.prototype.getCurrentTest = function() {
return this.getCurrentSuite().currentTest;
};
Allure.prototype.startSuite = function(suiteName, timestamp) {
this.suites.unshift(new Suite(suiteName,timestamp));
};
Allure.prototype.endSuite = function(timestamp) {
var suite = this.getCurrentSuite();
suite.end(timestamp);
if(suite.hasTests()) {
writer.writeSuite(this.options.targetDir, suite);
}
this.suites.shift();
};
// other methods
module.exports = Allure;
And the Suit.js is
function Suite(name, timestamp){
this.name = name;
this.start = timestamp || Date.now();
this.testcases = [];
}
Suite.prototype.end = function(timestamp) {
this.stop = timestamp || Date.now();
};
Suite.prototype.hasTests = function() {
return this.testcases.length > 0;
};
Suite.prototype.addTest = function(test) {
this.testcases.push(test);
};
Suite.prototype.toXML = function() {
var result = {
'#': {
'xmlns:ns2' : 'urn:model.allure.qatools.yandex.ru',
start: this.start
},
name: this.name,
title: this.name,
'test-cases': {
'test-case': this.testcases.map(function(testcase) {
return testcase.toXML();
})
}
};
if(this.stop) {
result['#'].stop = this.stop;
}
return result;
};
module.exports = Suite;
How can I set browserName at the end of the suit.fullname. Is there any alternative way to do that.
Error
Test Suites & Specs:
1. 0030 Test for correct login
No specs found
Finished in 0.017 seconds
An error was thrown in an afterAll
AfterAll TypeError: Cannot read property 'end' of undefined
>> Done!
Summary:
Finished in 0.017 seconds
chrome
[13:23:03] E/launcher - Cannot read property 'startSuite' of undefined
[13:23:03] E/launcher - TypeError: Cannot read property 'startSuite' of
undefined
at C:\Users\mnowshin\projects\beck-hb\hb-frontend\protractor.mn.conf.js:130:18
at ManagedPromise.invokeCallback_ (\node_modules\selenium-webdriver\lib\promise.js:1376:14)
at TaskQueue.execute_ (\node_modules\selenium-webdriver\lib\promise.js:3084:14)
at TaskQueue.executeNext_ (\node_modules\selenium-webdriver\lib\promise.js:3067:27)
at asyncRun (\node_modules\selenium-webdriver\lib\promise.js:2927:27)
at \node_modules\selenium-webdriver\lib\promise.js:668:7
at process.internalTickCallback (internal/process/next_tick.js:77:7)
[13:23:03] E/launcher - Process exited with error code 199
Process finished with exit code 199
so I don't work with allure, but I can show you how I get the browser name and then you can try to apply to your case.
Try to change from this:
var browserNameforSuit;
let bName = (async () => {
try {
browserNameforSuit = (await browser.getCapabilities()).get('browserName');
return browserNameforSuit;
} catch (err) {
return "Error or smth"
}
})();
To this:
var capsPromise = browser.getCapabilities();
var browserNameforSuit;
capsPromise.then(function (caps) {
browserNameforSuit = caps.get('browserName');
//if you need you can return the result to a var in order to user somewhere else
});
This is the onComplete from my conf.js
onComplete: function()
{
var browserName, browserVersion;
var capsPromise = browser.getCapabilities();
capsPromise.then(function (caps) {
browserName = caps.get('browserName');
browserVersion = caps.get('version');
platform = caps.get('platform');
//here I prepare the testConfig adding what I need//
//here I get the xml with the test results and create an html
new HTMLReport().from('./execution_results/reports/xml/xmlresults.xml', testConfig);
});//End Of capsPromise
},
Hope it helps!

How to return templates from cache or ajax load?

In my code I try to load templates from cache. If template does not present in cache - load template from server by ajax. When loading is finished, I want to put template to cache and return it. Here is it:
var manager = function () {
return {
cache: [],
getTemplate: function (templateId) {
this.templateId = templateId;
if (this.cache[this.templateId]) {
return this.cache[this.templateId];
}
return this.loadTemplate();
},
loadTemplate: function() {
var
self = this;
$.get('/assets/templates/' + this.templateId + '.html', function (templateHtml) {
self.cache[self.templateId] = templateHtml;
return self.getTemplate(self.templateId);
});
}
}
}
var
manager = manager();
$('body').append( manager.getTemplate('template') );
I know that my code does not working because ajax request finished after function loadTemplate end. I think code can be fixed with deferred object but don't know how. Can anyone help me to find a solution?
There are two way of achieving your goal:
Promises (there are a lot of libs/shims). I'll rewrite it to ES6 just for the learning:
let manager = function () {
return {
cache: [],
getTemplate(id) {
let cache = this.cache;
return new Promise((resolve, reject) => {
if (cache[id]) {
resolve(cache[id]);
} else {
this.loadTemplate(id)
.then(template => {
cache[id] = template;
resolve(template);
})
.fail(reject);
}
});
},
loadTemplate(id) {
return $.get('/assets/templates/' + id + '.html');
}
}
};
let manager = manager();
manager.getTemplate('template').then((template) => {
$('body').append(template);
});
Callbacks:
let manager = function () {
return {
cache: [],
getTemplate(id, cb) {
let cache = this.cache;
if (cache[id]) {
cb(cache[id]);
} else {
this.loadTemplate(id)
.then(template => {
cache[id] = template;
cb(template);
});
}
},
loadTemplate(id) {
return $.get('/assets/templates/' + id + '.html');
}
}
};
let manager = manager();
manager.getTemplate('template', (template) => {
$('body').append(template);
});
Here's how you would do it, supporting all major browsers, and caching the requests too. This way you will only perform 1 request per template. (The other answers only cache the response).
var Manager = function() {
return {
cache: [],
getTemplate(id) {
var that = this;
if (that.cache[id] && that.cache[id].then){
console.log("Promise cache");
return that.cache[id]; //return promise from cache
}
return $.Deferred(function() {
var def = this;
if (that.cache[id]) {
console.log("Retrieved from cache!");
return def.resolve(that.cache[id]); //return cached template
}
that.cache[id] = def; //Cache promise
console.log("Retrieving template...");
that.loadTemplate(id).then(function(template) {
that.cache[id] = template;
def.resolve(template)
}).fail(function() {
def.reject();
});
return that.cache[id]; //return promise
}).promise();
},
loadTemplate(id) {
return $.get('https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js');
}
}
};
var manager = Manager();
manager.getTemplate('template').then(function(template){
console.log("loaded 1");
});
//This will use the promise from the first call (1 Request only)
manager.getTemplate('template').then(function(template){
console.log("loaded 2");
manager.getTemplate('template').then(function(template){
console.log("loaded 3"); //This will be retrieved fully from cache
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
As you are fetching the template via AJAX, you will be able to append the result only in AJAX success. So you need to pass the append logic as callback.Check the below code.
var manager = function () {
return {
cache: [],
getTemplate: function (templateId,callback) {
this.templateId = templateId;
if (this.cache[this.templateId]) {
callback(this.cache[this.templateId]);
}
this.loadTemplate(callback);
},
loadTemplate: function(callback) {
var
self = this;
$.get('/assets/templates/' + this.templateId + '.html', function (templateHtml) {
self.cache[self.templateId] = templateHtml;
callback(templateHtml)
});
}
}
}
var
manager = manager();
manager.getTemplate('template',function(result) {
$('body').append( result );
});
You may not need 2 functions to do this. So you can make it as one

Module export multiple classes

This is /cars/ford.js
Ford = Car.extend({
classId: 'Ford',
init: function (driver) {
this._driver = driver;
Car.prototype.init.call(this);
tick: function (ctx) {
Car.prototype.tick.call(this, ctx);
},
destroy: function () {
if (this._driver !== undefined) {
this._driver._license.pull(this);
}
Car.prototype.destroy.call(this);
}
});
if (typeof(module) !== 'undefined' && typeof(module.exports) !== 'undefined') {
module.exports = Ford;
}
This is /cars/knightrider.js:
KITT = Car.extend({
classId: 'KITT',
init: function () {
Car.prototype.init.call(this);
var self = this;
},
newDirection: function () {
var self = this;
this._move.direction()
.duration(Math.random())
.properties({
x: Math.random(),
y: Math.random(),
})
.voice('robotic')
.afterDirection(function () {
self.newDirection();
})
.start();
}
});
if (typeof(module) !== 'undefined' && typeof(module.exports) !== 'undefined') {
module.exports = KITT;
}
I want to have all cars inside the same file to preserve my self sanity. How can I wrap them without altering my classes? Feel free to recommend any 'proper packaging' for Javascript functions book or tutorial, because I really dislike to open files. When I'm editing a car, I might want to edit other one.
Wish I could do:
module.exports.Allmycars = KITT, Ford;
And then call them with:
Allmycars.Ford
A solution could be :
//cars/index.js
module.exports = {
KITT:require("./knightrider"),
Ford:require("./ford")
}
//So u could use :
var allMyCars = require("./cars");
var ford = new allMyCars.Ford();

Angular: Return data only once several promises are fulfilled

I want to return data from a Service function and inject it into a controller via the routeProvider, but only once the data has been fully loaded.
app.js:
.when('/sources', {
templateUrl: 'views/app/source/inventory.html',
controller: 'InventoryCtrl',
pageTitle: 'Data Inventory',
resolve: {
orders: function (OrderService) {
return OrderService.getOrders();
},
sources: function (SourceService) {
return SourceService.getSources();
}
}
})
SourceService:
angular.module('wp.source')
.factory('SourceService', function ($http, $q) {
return {
getSources: function () {
var model = {
sources: []
};
var promises = [];
return $http.get('datasets/project.json')
.then(function (data) {
if(localStorage['files'] != null) {
var files = JSON.parse(localStorage['files']);
for (var i = 0; i < files.length; i++) {
model.sources.push(files[i]);
}
}
var sources = [];
for (var sourceId in data.data.sources) {
var source = data.data.sources[sourceId];
source.id = sourceId;
sources.push(source);
}
angular.forEach(sources, function (source) {
promises.push($http.get('datasets/' + source.filename).then(function (datasource, resolve) {
var metadata = datasource.data.metadata;
var source = {
name: metadata.name,
address: metadata.url,
frequency: metadata.refresh,
type: metadata.type,
tags: metadata.tags,
size: metadata.size,
active: source.active,
sourceID: source.id,
sourceFile: source.filename
};
return source;
}));
});
//var finalPromise = sources.reduce(function (promise, source, index, array) {
// return promise.then(function (a) {
// var query = $http.get('datasets/' + source.filename)
// .then(function (datasource) {
// var metadata = datasource.data.metadata;
// model.sources.push({
// name: metadata.name,
// address: metadata.url,
// frequency: metadata.refresh,
// type: metadata.type,
// tags: metadata.tags,
// size: metadata.size,
// active: source.active,
// sourceID: source.id,
// sourceFile: source.filename
// });
// });
// });
//}, $q.when([]));
//
//finalPromise.finally(function () {
// return model;
//})
$q.all(function () {
return model;
});
});
}
}
});
Controller:
angular.module('wp.source')
.controller('InventoryCtrl', function ($scope, orders, sources) {
$scope.orders = orders.data;
$scope.gridView = true;
$scope.rowView = false;
$scope.allSources = sources.sources;
$scope.shownSources = $scope.allSources;
...
Currently I have the problem that the value is injected before "model" is fully loaded. I tried to pack the subqueries into a $q.all promise, but I don't know exactly where to go from there
Any help would be appreciated
You want to use $q.defer() and returned the deferred promise object.
You code will look something like this.
angular.module('wp.source')
.factory('SourceService', function ($http, $q) {
return {
getSources: function () {
var model = {
sources: []
};
var deffered = $q.defer();
var promises = [];
return $http.get('datasets/project.json')
.then(function (data) {
if(localStorage['files'] != null) {
var files = JSON.parse(localStorage['files']);
for (var i = 0; i < files.length; i++) {
model.sources.push(files[i]);
}
}
var sources = [];
for (var sourceId in data.data.sources) {
var source = data.data.sources[sourceId];
source.id = sourceId;
sources.push(source);
}
angular.forEach(sources, function (source) {
promises.push($http.get('datasets/' + source.filename).then(function (datasource, resolve) {
var metadata = datasource.data.metadata;
var source = {
name: metadata.name,
address: metadata.url,
frequency: metadata.refresh,
type: metadata.type,
tags: metadata.tags,
size: metadata.size,
active: source.active,
sourceID: source.id,
sourceFile: source.filename
};
return source;
}));
});
//var finalPromise = sources.reduce(function (promise, source, index, array) {
// return promise.then(function (a) {
// var query = $http.get('datasets/' + source.filename)
// .then(function (datasource) {
// var metadata = datasource.data.metadata;
// model.sources.push({
// name: metadata.name,
// address: metadata.url,
// frequency: metadata.refresh,
// type: metadata.type,
// tags: metadata.tags,
// size: metadata.size,
// active: source.active,
// sourceID: source.id,
// sourceFile: source.filename
// });
// });
// });
//}, $q.when([]));
//
//finalPromise.finally(function () {
// return model;
//})
$q.all(promises).then(function () {
deferred.resolve(model);
});
});
}
return deferred.promise();
}
});
EDIT As others have mentioned you need to pass your array of promises into $q.all to get this to work right. Also it's worth noting that $q.all will return an array with the results of each promise in the order they were in the promise array.
Here's a plunker demoing the use of defer.
There's more $q.defer and $q.all here in the Angular's Docs.
You will need to use $q.all with array of promises and return it from outer promise:
angular.module('wp.source')
.factory('SourceService', function($http, $q) {
return {
getSources: function() {
var model = {
sources: []
};
return $http.get('datasets/project.json').then(function(data) {
if (localStorage['files'] != null) {
var files = JSON.parse(localStorage['files']);
for (var i = 0; i < files.length; i++) {
model.sources.push(files[i]);
}
}
var sources = [],
promises = [];
for (var sourceId in data.data.sources) {
var source = data.data.sources[sourceId];
source.id = sourceId;
sources.push(source);
}
var promises = sources.map(function(source) {
return $http.get('datasets/' + source.filename).then(function(datasource, resolve) {
var metadata = datasource.data.metadata;
model.sources.push({
name: metadata.name,
address: metadata.url,
frequency: metadata.refresh,
type: metadata.type,
tags: metadata.tags,
size: metadata.size,
active: source.active,
sourceID: source.id,
sourceFile: source.filename
});
});
});
return $q.all(promises).then(function() {
return model;
});
});
}
}
});
You are not using $q.all correctly. The simplest possible fix to your code would be changing
$q.all(function () {
return model;
});
to
$q.all(promises).then(function() {
return model;
});
You're basically not "telling" $q.all which promises it needs to aggregate. Please refer to the documentation for $q and check how all works.

Javascript simple MVC + module pattern implementation

Here is a very basic attempt to create a "hello world"-like JS app using the module and MVC patterns.
var appModules = {};
appModules.exampleModul = (function () {
var _data = ['foo', 'bar']; // private variable
return {
view: {
display: function() {
$('body').append(appModules.exampleModul.model.getAsString());
},
},
model: {
getAsString: function() {
return _data.join(', ');
},
}
};
})();
appModules.exampleModul.view.display();
This works fine, but I'm not happy how I have to reference the model function from the view, using the full object path: appModules.exampleModul.model.getAsString(). How can I expose the public model methods to the view, so I could simply use something like model.getAsString()? Or do I need to organize the code differently?
One option is you can convert those objects into private implementations.
appModules.exampleModul = (function() {
var _data = ['foo', 'bar'];
// private variable
var _view = {
display : function() {
$('body').append(_model.getAsString());
},
};
var _model = {
getAsString : function() {
return _data.join(', ');
},
};
return {
view : _view,
model : _model
};
})();
You could do something like this:
var appModules = {};
appModules.exampleModul = (function () {
var _data = ['foo', 'bar']; // private variable
return {
view: {
display: function() {
$('body').append(this.model.getAsString());
},
},
model: {
getAsString: function() {
return _data.join(', ');
},
}
};
})();
var display = appModules.exampleModul.view.display.bind(appModules.exampleModul);
display();
Which isn't really the prettiest of solutions, but does offer a more generic solution inside the display function!

Categories

Resources