Regarding $location.$$absUrl multiple condition - angularjs

Can we check condition like below if not then what is other way??? I need to show console.log only below URl.
$location.$$absUrl.includes('localhost' || 'qa1' || 'cat1')
Below are my URL:
https://localhost:9005/
https://qa1.abc.com/
https://cat1.abc.com/

to achieve what you expect, you should use regex pattern.
// If my domain contain localhost OR qua1 or cat1.
if (!$location.absUrl().match(/(localhost|qa1|cat1)/)) {
// then override console.log to empty function.
console.log = function(){ };
//Is equal to do : window['console']['log'] = function(){ };
}
UPDATE
if you want to disable all kind of console output check this other topic

Try:
var absUrl = $location.absUrl();
if(absUrl.match(/(localhost|qa1|cat1)/)){
console.log(absUrl)
}

Related

Update global variable with protractor on non-Angular page

Right to it. I have a global variable which I would like to export, so I can use the value in following specs. But since protractor is not working synchronously, export happens before the variable gets updated to the right value.
The action is a click on button where player gets created and I need the username to be exported.
Console.log contains the right value and also export happens as it should, only thing, that it exports the hardcoded value or undefined if I set globalUsername = "";
Anyone can help on how to make this export sinchronized, so it will wait for all describes to finish up or the variable to get updated.
describe ("Quick add player", function() {
it ("New player is created and credentials are displayed", function() {
browser.ignoreSynchronization = true;
var playerData1 = playerData.getText().then(function(text) {
console.log("1: ", text);
return text.split("\n")[0];
//console.log(text.split(" ")[1]);
});
var globalUsername = "1234";
playerData1.then(function(text) {
globalUsername = text.split(" ")[1];
//expect(globalUsername).not.toEqual("");
console.log("*****************\n" + globalUsername);
});
});
});
module.exports = globalUsername;
Because you are using browser ignore synchronization, your tests will probably have nested then statements because you need to wait for promises to resolve. Instead of creating multiple then statements, you could put them into a single then statement. Also, you could tie your variable to the global namespace with global.username = username or add it to the global browser object with browser.username = username
describe ("Quick add player", function() {
it ("New player is created and credentials are displayed", function() {
browser.ignoreSynchronization = true;
global.username = "1234";
playerData.getText().then(function(text) {
console.log("1: ", text);
global.username = text.split("\n")[0].split(" ")[1];
console.log("*****************\n" + global.username);
});
});
});
// access using global.username tied to the global namespace
// instead of var username = require('thisfile');
So this solved my issue, where I export a string and not the variable....searched google with wrong tags.
This is placed outside the first describe at the bottom.
module.exports = {
exportUsername: function () {
return globalUsername;
}
};

Prevent search changes from spamming history

I'm using links like #!/OrderList?id=123, which shows the list of all orders and more details of the order 123. With reloadOnSearch=false and watching $routeUpdate it works fine, except for one thing: All such links get put into the browsers history, while I'd prefer to have only one such link there. For example, instead of
#!/OrderList?id=123
#!/OrderList?id=124
#!/OrderList?id=125
#!/AnotherList?id=678
#!/AnotherList?id=679
just the last member of each group, i.e.,
#!/OrderList?id=125
#!/AnotherList?id=679
I'm aware of $location.replace(), but I can't see where to place it when the change happens via following a link. I tried to place it in $scope.$on("$routeUpdate", ...), but it did nothing, probably because it's too late when the route has already changed.
I'm not using neither router-ui nor the HTML5 mode (just plain angular-route).
I'm afraid, I wasn't clear about me insisting on using href rather than a custom handler. I want the links to work with middle mouse click and bookmarks and everything. A combination of ng-href and ng-click might do what I want, but I've found a simple solution working with plain links.
Looks like you may want to update the URL query parameter using an ng-click function instead of relying on a link, then call a function like the one below to update the parameter... With replace state, the history should only track the current value. I haven't tested this case so if you try it, let me know if it works.
function changeUrlParam (param, value) {
var currentURL = window.location.href;
var urlObject = currentURL.split('?');
var newQueryString = '?';
value = encodeURIComponent(value);
if(urlObject.length > 1){
var queries = urlObject[1].split('&');
var updatedExistingParam = false;
for (i = 0; i < queries.length; i++){
var queryItem = queries[i].split('=');
if(queryItem.length > 1){
if(queryItem[0] == param){
newQueryString += queryItem[0] + '=' + value + '&';
updatedExistingParam = true;
}else{
newQueryString += queryItem[0] + '=' + queryItem[1] + '&';
}
}
}
if(!updatedExistingParam){
newQueryString += param + '=' + value + '&';
}
}else{
newQueryString += param + '=' + value + '&';
}
window.history.replaceState('', '', urlObject[0] + newQueryString.slice(0, -1));
}
Maybe what you can do is, istead of a regular <a ng-href="#!/OrderList?id={{your.id}}">Link to your ID</a> you can create a link with an ng-clickdirective bound to a function which retrieves the data and passes it to the view.
Your HTML
`<span ng-click="loadListItem(your.id)">Link to your ID</span>`
<div id="your-item-data">
{{item.id}} - {{item.name}}
</div>
Your controller
myApp.controller('someController', function($scope) {
$scope.loadListItem(itemId) = function (
var myItem;
// Get item by 'itemId' and assign it to 'myItem' var
$scope.item = myItem;
);
});
This way instead of changing your URL, you can retrieve the item data in your controller and pass it to your view.
You don't give much detail of your controller/service implementation, but I hope this helps.
I think you were on the right track with the $scope.$on("$routeUpdate", ...) thing. Rather than $routeUpdate, however, try binding on $routeChangeStart:
$scope.$on("$routeChangeStart", function(event, nextRoute, currentRoute){
if (nextRoute.yourCriteria === currentRoute.yourCriteria){
//do your location replacement magic
}
});
If you wanted, you could even define a dontUpdateHistory boolean property in your route definitions, and then check for that property in your run block:
myApp.config(function($routeProvider){
$routeProvider.when('/whatever' {
templateUrl: 'whatever',
dontUpdateHistory: true //something like this
});
}).run(function($rootScope){
$rootScope.on('$routeChangeStart', function(event, nextRoute, currentRoute){
if (nextRoute.dontUpdateHistory){
//do your location replacement magic
}
});
I haven't tested any of this, but hopefully it gets the idea across.
I wasn't satisfied with any answer and after quite some debugging I found this solution:
.run(function($rootScope, $location) {
var replacing;
$rootScope.$on("$locationChangeStart", function(event, newUrl, oldUrl) {
if (oldUrl === newUrl) return; // Nobody cares.
// Make urls relative.
var baseLength = $location.absUrl().length - $location.url().length;
newUrl = newUrl.substring(baseLength);
oldUrl = oldUrl.substring(baseLength);
// Strip search, leave path only.
var newPath = newUrl.replace(/\?.*/, "");
var oldPath = oldUrl.replace(/\?.*/, "");
// Substantial change, history should be written normally.
if (oldPath !== newPath) return;
// We're replacing, just let it happen.
if (replacing) {
replacing = false;
return;
}
// We're NOT replacing, scratch it ...
event.preventDefault();
// ... and do the same transition with replace later.
$rootScope.$evalAsync(function() {
$location.url(newUrl).replace();
replacing = true;
});
});
})

Is it possible to prevent changing of router by clicking back button?

I'am building single page using BackboneJS and I need to prevent router executing on back button in a browser. To be exact I need to show confirmation custom popup with the text "Do you really want exit room? [yes|no]". So if user clicks yes then default actions should happens but if no then user should stay in the current screen.
I use Backbone.router with pushState: true. Does Backbonejs provide something like before router event to be possible prevent router handling or how could I archive it?
I'm not sure if this is still an issue, but this is how I would get around it. It may not be the best way, but could be a step in the right direction.
Backbone.History.prototype.loadUrl = function (fragment, options) {
var result = true;
if (fragment === void (0) && options === void (0) && this.confirmationDisplay !== void(0))
{
result = confirm('Are you sure you want to leave this room?');
}
var opts = options;
fragment = Backbone.history.fragment = Backbone.history.getFragment(fragment);
if (result) {
this.confirmationDisplay = true;
return _.any(Backbone.history.handlers, function (handler) {
if (handler.route.test(fragment)) {
//We just pass in the options
handler.callback(fragment, opts);
return true;
}
});
}
return this;
}
Essentially checking if we have a fragment and options, if not, we can assume the app just started, or the user clicked the back button.
Backbone router has an execute method which is called for every route change, we can return false to prevent the current transition. The code will probably look like below :
With an asynchronous popup (untested code, but should work)
Backbone.Route.extend({
execute: function(callback,args){
if(this.lastRoute === 'room'){
showPopup().done(function(){
callback & callback.apply(this,args);
}).fail(function(){
Backbone.history.navigate('room/486',{trigger:false});
});
}else{
callback && callback.apply(this,args);
}
},
showPopup: function(){
var html = "<<div><p>Do you really want to exit</p><button id='yes'>Yes</button><button id='no'>No</button></div>"
var promise = $.Deferred();
$('body').append(html);
$(document).on('click','button#yes',function(){
promise.resolve();
});
$(document).on('click','button#no',function(){
promise.reject();
});
return promise;
}
});
With synchronous confirm popup
Backbone.Route.extend({
execute: function(callback,args){
if(this.lastRoute === 'room'){
var conf = confirm("Do you really want to exit the room ?");
if(!conf){
//Change the route back to room
Backbone.history.navigate('room/486',{trigger:false});
return false;
}
};
callback && callback.apply(this,args);
}
});
References:
http://backbonejs.org/#Router-execute

How can I grab all the content/text on a page inside a protractor test?

I want to set a variable based on whether some text is present on the current page or not.
And then run a protractor test that depends on that variable. I can't do $(':contains') because there's $ != jQuery in this context and i can't see a simple way to do it with getText() which returns a promise. Is there a matcher like expect.toContain? and a way to run some code after that matcher is run? Or is there some other strategy i can use.
if i think i understand you correctly you could use these following lines :
this will give all the text from the page :
window.document.getElementsByTagName("html")[0].innerText
and then you could use a regex to check for your text like this :
var res = patt.test(str);
this is sort of what you get :
for this example i am searching for the text "status": "ok"
var str = window.document.getElementsByTagName("html")[0].innerText;
var patt = /"status": "ok"/;
var res = patt.test(str);
if(res){console.log(text is present!)}
you want something a bit like this:
// utility to test expection of an element to match a regex:
var expectByCssToMatch = function(css, pattern) {
browser.driver.findElement(
by.css(css)).getText().then(function(text) {
expect(text).toMatch(pattern);
});
};
usage:
describe('Logging in, function() {
it('should work', function() {
var namePattern = new RegExp(param.name, i');
expectByCssToMatch('.messages', /log\s?in successful/i);
expectByCssToMatch('body', namePattern);
});
});
to achieve what you're asking you'd want to replace the expect() call with a callback of your own choice

How do I change AngularJS ng-src when API returns null value?

In working with the API from themoviedb.com, I'm having the user type into an input field, sending the API request on every keyup. In testing this, sometimes the movie poster would be "null" instead of the intended poster_path. I prefer to default to a placeholder image to indicate that a poster was not found with the API request.
So because the entire poster_path url is not offered by the API, and since I'm using an AngularJS ng-repeat, I have to structure the image tag like so (using dummy data to save on space):
<img ng-src="{{'http://example.com/'+movie.poster_path}}" alt="">
But then the console gives me an error due to a bad request since a full image path is not returned. I tried using the OR prompt:
{{'http://example.com/'+movie.poster_path || 'http://example.com/missing.jpg'}}
But that doesn't work in this case. So now with the javascript. I can't seem to get the image source by using getElementsByTagName or getElementByClass, and using getElementById seems to only grab the first repeat and nothing else, which I figured would be the case. But even then I can't seem to replace the image source. Here is the code structure I attempted:
<input type="text" id="search">
<section ng-controller="movieSearch">
<article ng-repeat="movie in movies">
<img id="myImage" src="{{'http://example.com/'+movie.poster_path}}" alt="">
</article>
</section>
<script>
function movieSearch($scope, $http){
var string,
replaced,
imgSrc,
ext,
missing;
$(document).on('keyup', function(){
string = document.getElementById('search').value.toLowerCase();
replaced = string.replace(/\s+/g, '+');
$http.jsonp('http://example.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
});
imgSrc = document.getElementById('myImage').src;
ext = imgSrc.split('.').pop();
missing='http://example.com/missing.jpg';
if(ext !== 'jpg'){
imgSrc = missing;
}
});
}
</script>
Any ideas with what I'm doing wrong, or if what I'm attempting can even be done at all?
The first problem I can see is that while you are setting the movies in a async callback, you are looking for the image source synchronously here:
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
});
// This code will be executed before `movies` is populated
imgSrc = document.getElementById('myImage').src;
ext = img.split('.').pop();
However, moving the code merely into the callback will not solve the issue:
// THIS WILL NOT FIX THE PROBLEM
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
// This will not solve the issue
imgSrc = document.getElementById('myImage').src;
ext = img.split('.').pop();
// ...
});
This is because the src fields will only be populated in the next digest loop.
In your case, you should prune the results as soon as you receive them from the JSONP callback:
function movieSearch($scope, $http, $timeout){
var string,
replaced,
imgSrc,
ext,
missing;
$(document).on('keyup', function(){
string = document.getElementById('search').value.toLowerCase();
replaced = string.replace(/\s+/g, '+');
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
$scope.movies.forEach(function (movie) {
var ext = movie.poster_path && movie.poster_path.split('.').pop();
// Assuming that the extension cannot be
// anything other than a jpg
if (ext !== 'jpg') {
movie.poster_path = 'missing.jpg';
}
});
});
});
}
Here, you modify only the model behind you view and do not do any post-hoc DOM analysis to figure out failures.
Sidenote: You could have used the ternary operator to solve the problem in the view, but this is not recommended:
<!-- NOT RECOMMENDED -->
{{movie.poster_path && ('http://domain.com/'+movie.poster_path) || 'http://domain.com/missing.jpg'}}
First, I defined a filter like this:
In CoffeeScript:
app.filter 'cond', () ->
(default_value, condition, value) ->
if condition then value else default_value
Or in JavaScript:
app.filter('cond', function() {
return function(default_value, condition, value) {
if (condition) {
return value;
} else {
return default_value;
}
};
});
Then, you can use it like this:
{{'http://domain.com/missing.jpg' |cond:movie.poster_path:('http://domain.com/'+movie.poster_path)}}

Resources