How to access object created in one describe from a different describe? - javascript

I am having some trouble currently with a project I am working on. Essentially I am trying to run multiple tests from different files together in separate describe blocks.
In the first test I would like to to set up all of the parameters and then in the second test I would like to use those parameters to fill out a form. With the way promises and describes work I am wondering if this is even possible. Does anyone have any suggestions?
I haven't tried much other than trying to make my objects global which I would prefer to stay away from and passing my objects into the second describe which doesn't work since it gets passed in before the first describe finishes and actually assigns value to them.
describe('0', function () {
let objectToPassAround = {}
describe('1', function () {
const test1 = require('#file1');
test1.runTest();
});
describe('2', function () {
const test2 = require('#file2');
test2.runTest();
});
});
For example, in the above, I would want to set the value of objectToPassAround in test1 and use that object, with it's newly set values, in test2.
Thanks!

You can wrap all of it in a bigger describe.
describe('0', () => {
let objectToPassAround = {};
describe('1', () => {
// you should have access to objectToPassAround
});
describe('2', () => {
// you should have access to objectToPassAround
});
});
Something to be careful is that if you're using Jasmine, the tests may run in random order and so if you set the object in test 3 and then expect for it to be set in test 4, it may not be the case. Test 4 can run before test 3. This is a feature to ensure the order of the tests do not matter since each one should be independent of each other.

Related

Inject a function/property to another function/object that can be called or read/set from functions within that function?

Can I, in JavaScript, add a function to an already existing function or object that a function within that object then "suddenly" can "see" and call itself? Here is an example to demonstrate:
function CreateThing() {
function callAddedFunction() {
theFunction(); // this does not exist yet!
}
}
So theFunction() obviously does not exist in createThing(). Is there any way to add that outside so that when I then invoke callAddedFunction() it is able to resolve that? Something like:
let f = new CreateThing();
addFunctionAtRuntime(f, "theFunction", function() {
console.log("YAY!");
};
f.callAddedFunction();
I have tried to experiment with prototype, but I have been unable to do this. Note that the main reason for me wanting to do this is "fake" object inheritance without resorting to classes and inheritance as that requires the this keyword in front of every function call. I also want to avoid having to pass an object in that function as a parameter that can be called through in order to reach those other functions. I know that I can achieve this by having all those extra functions in global scope, but I have hoped to avoid that if possible.
EDIT: I have modified my example with the magic function I was looking for called addFunctionAtRuntime which from what I have understood is not possible. Some suggest I use eval and just make those functions available in the eval script, but so far I have been able to do this by creating a script tag dynamically and add my code as content including those functions I wanted callAddedFunction() in my example above to be able to see (without having to call through some object context).
I'm not sure this is exactly what you want but you can also use a generic higher-order function that returns the implementation you are looking for.
const supplimentor = (src, extraFunc) => ({
src: new src(),
extraFunc
})
//OR
function supplimentor1(src, extraFunc) {
this.extraFunc = extraFunc;
new src();
}
function CreateThing() {console.log('SOURCE')}
const extraFunc = () => console.log('EXTRA');
const newFunc = supplimentor(CreateThing, extraFunc)
newFunc.extraFunc()
const newFunc1 = new supplimentor1(CreateThing, extraFunc)
newFunc1.extraFunc()
Just in case the OP ...
... is not in need of something as complex as method modification as described / demonstrated at e.g.
"Can I extend default javascript function prototype to let some code been executed on every function call?"
"Intercepting function calls in javascript" ...
... why doesn't the OP just provide the very function object as parameter to the Thing constructor at the thing object's instantiation time?
After all it comes closest to (or is exactly) what the OP describes with ...
Can I, in JavaScript, add a function to an already existing function or object that a function within that object then "suddenly" can "see" and call itself?
function Thing(fct) {
this.callAddedFunction = () => fct();
}
const thing = new Thing(() => console.log("YAY!"));
thing.callAddedFunction();

How to call a module again in Javascript/Backbone which is already loaded

I am new to Javascript/backboneJS/RequireJS. In order to render my front end I have made one controller, one model and one view. Also, I have one dropdown in my html file.
So what I have done till now is in my html file at the END I have
require(['common'],function()
{
require(['jquery','fastclick','nprogress','charts','underscore','spinner'],function()
{
require(['firstDashboardController']);
});
});
So I am loading "firstDashboardController" and this controller loads all the modules accordingly and displays data in front end. So everything works fine.
Now I have a dropdown in the front end. When I select the dropdown, as per the id selected I want to retrieve the data. So I need to call "firstDashboardController" again so that everything gets rendered as per the new id that I have got.
So what am I suppose to do? Like do I need to UN-REQUIRE my "firstDashboardController" and then REQUIRE it again passing the new id. Because the controller is already loaded via Require beacuse I loaded it in my HTML file as mentioned above. But I need to load it again as per the new id selected it via dropdown. So how to go about it? Pleas help me. if any code snippet is required I can put that.
Code Snippet:
My Controller:
define(['backbone', 'firstSubViewModel','dropdownViewModel', 'dropdownModel'],
function(Backbone, firstSubViewModel, dropdownViewModel, dropdownModel) {
var ch = new dashboardModel.chart({});
if (localStorage.getItem('p_kt') && localStorage.getItem('p_acid') && localStorage.getItem('p_seid')) {
var data = {
tokenProp: localStorage.getItem('p_kt'),
accountIdProp: localStorage.getItem('p_acid'),
sessionIdProp: localStorage.getItem('p_seid')
};
$.when(
ch.fetch(data) // GETTING DATA FROM MODEL VIA AJAX CALL in my model.JS
).then(function() {
// Main Graph on Dashboard
new firstSubViewModel({
el: '#chartAnchor1',
model: ch
});
});});
I somehow need to call ch.fetch() again.
You aren't properly defining your controller. You currently have it as sort of a one-time setup method instead of something you can re-run later. Let's go step by step.
myLife.js:
define([], function() {
return "a complex series of failures";
});
By returning a value from define's callback, this defines that anytime I require "myLife", then it will provide "a complex series of failures" in the callback function. This is what Backbone and other AMD modules do to appear inside your code blocks. However, it only runs the contents once; and saves the result. So, this won't work:
incrementer.js:
var x = 0;
define([], function() {
x = x + 1;
return x;
});
(Trying to require incrementer would always give you "1".)
What you want to do is return a function - one you can re-run anytime.
incrementerV2.js:
define([], function() {
var x = 0;
return function() {
x = x + 1;
return x;
};
});
In any file, you can then have this:
require(['incrementerV2'], function(myIncr) {
myIncr(); // 1
myIncr(); // 2
});
...And, for the record, I would recommend having only one require statement in any given file whenever possible. You can add dependencies in that first-argument array if load order is important.
More commonly, people will have one module contain a self-defined object that has multiple functions in it, rather than the function I gave above. Returning and then using just one function is valid as well, depending on the use case. Any variable type works, but just remember it will always be the one and only same variable anytime you later need it.
Load Order
When the system retrieves myLife.js or incrementer.js above, there's an intermediate step before it actually runs the definition function we've defined. It will look at the first argument, the array of named dependencies, and figure out if there are still dependencies needed before it can run the function given. Example:
a.js: require(['b', 'c'], function(b, c) {
b.js: define(['c'], function(c) {
c.js: define([], function() {
a.js is requested first, but not run because it needs B and C. B loads next, but is ignored because C is not loaded. C runs, and then its return value is passed into A and B. This system is internally very smart, and should never request the same file twice or have conflicts if one file loads before another. You can use it much like imports in Java.
Also, let's say you only added 'c' in a.js so that b.js wouldn't crash, because it needs it loaded first - in that case, just take it out of A and it should work the same.
a.js: require(['b'], function(b) {
Just like A did, B will automatically load all its dependencies before it executes anything. A simple principle is to only refer a dependency if it's actually directly referenced in the file (or defines necessary global variables)

What's the recommended way to unit test a single method in a yeoman generator?

The doc for yeoman unit testing seems to be oriented around integration testing, namely running the entire generator and then examining the side effects produced i.e. for the existence of certain files. For this you can use helpers.run().
This is all fine and well, but I also want to be able to unit test a single method (or "priority") and test internal states of the generator i.e. internal vars. I have been able to do this before by using createGenerator like so:
subAngularGenerator = helpers.createGenerator('webvr-decorator:sub-angular', [
path.join(__dirname, '../generators/sub-angular')
],
null,
{'artifacts': artifacts, appName: APP_NAME, userNames: userNames,
});
This has no RunContext, but I can usually add enough things to the structure so that it will run. For instance:
// mixin common class
_.extend(subAngularGenerator.prototype, require('../lib/common.js'));
// we need to do this to properly feed in options and args
subAngularGenerator.initializing();
// override the artifacts hash
subAngularGenerator.artifacts = artifacts;
// call method
subAngularGenerator._injectDependencies(fp, 'controller', ['service1', 'service2']);
Which allows me to test internal state:
var fileContents = subAngularGenerator.fs.read(fp);
var regex = /\('MainCtrl', function \(\$scope, service1, service2\)/m;
assert(regex.test(fileContents));
This works fine as long as the method is basic javascript, like for/next loops and such. If the method make use of any 'this' variables, like this.async(), I get 'this.async' is not a function.
initialPrompt: function () {
var prompts = [];
var done = this.async(); //if this weren't needed my ut would work
...
I can manually add a dummy this.async, but then I go down the rabbit's hole with other errors, like 'no store available':
AssertionError: A store parameter is required
at Object.promptSuggestion.prefillQuestions (node_modules/yeoman-generator/lib/util/prompt-suggestion.js:98:3)
at RunContext.Base.prompt (node_modules/yeoman-generator/lib/base.js:218:32)
at RunContext.module.exports.AppBase.extend.prompting.initialPrompt (generators/app/index.js:147:12)
at Context.<anonymous> (test/test-app.js:158:42)
I tried to create a runContext and then add my generator to that:
var helpers = require('yeoman-generator').test;
// p.s. is there a better way to get RunContext?
var RunContext = require('../node_modules/yeoman-generator/lib/test/run-context');
before(function (done) {
appGenerator = helpers.createGenerator('webvr-decorator:app', [
path.join(__dirname, '../generators/app')
],
null,
appName: APP_NAME, userNames: userNames,
{});
app = new RunContext(appGenerator); //add generator to runContext
});
app.Generator.prompting.initialPrompt(); //gets async not defined
But this gets the same problem.
My theory is the problem has to with 'this' contexts. Normally the method runs with the 'this' context of the entire generator (which has a this.async etc), but when I run the method individually, the 'this' context is just that of the method/function itself (which has no async in its context). If this is true, then it's really more of a javascript question, and not a yeoman one.
It seems like there should be an easy way to unit test individual methods that depend on the generator context such as calls to this.async. I referred to generator-node as an example of best practices, but it only appears to be doing integration testing.
Does anyone have any better ideas, or do I need to just keep futzing around with JavaScript techniques?
Many Thanks.
I was able to get it to work, but it's a total hack. I was able to decorate a RunContext with the necessary artifacts, and then using apply, I put my generator in the context of the RunContext:
var appGenerator;
var app;
before(function (done) {
// create a generator
appGenerator = helpers.createGenerator('webvr-decorator:app', [
path.join(__dirname, '../generators/app')
],
null,
appName: APP_NAME, userNames: userNames,
{}
);
// get a RunContext
app = new RunContext(appGenerator);
// the following did *not* work -- prompts were not auto-answered
app.withPrompts({'continue': true, 'artifactsToRename': {'mainCtrl' : 'main'}});
//add the following functions and hashes from the generator to the RunContext
app.prompt = appGenerator.prompt;
app._globalConfig = appGenerator._globalConfig;
app.env = appGenerator.env;
// the following two lines are specific to my app only
app.globals = {};
app.globals.MAIN_CTRL = 'main';
done();
});
it('prompting works', function () {
// Run the generator in the context of RunContext by using js 'call'
appGenerator.prompting.initialPrompt.call(app);
}
I no longer get any 'missing functions' messages, but unfortunately the prompts are not being automatically provided by the unit test, so the method stops waiting for something to feed the prompts.
The big "secret" was to call with apply which you can use to override the default this context. I put the generator in the context of the RunContext, which verifies my theory that the problem is about being in the improper context.
I assume there's a much better way to do this and that I'm totally missing something. But I thought I'd at least document what I had to do to get it to work. In the end, I moved the variable initialization code from the 'prompting'method, into the 'initializing' method, and since my 'intializing' method has no Yeoman runtime dependencies, I was able to use a simple generator without a RunContext. But that was just fortuitous in this case. In the general case, I would still like to find out the proper way to invoke a single method.

How to test 'private' functions in an angular service with Karma and Jasmine

I have a service in my angular app that looks something like this:
angular.module('BracketService', []).factory('BracketService', [function() {
function compareByWeight(a, b) {
return a.weight - b.weight;
}
function filterWeightGroup(competitors, lowWeight, highWeight) {
//filter stuff
}
function createBracketsByWeightGroup(weightGroup) {
//create some brackets
}
//set some base line values
var SUPER_HEAVY_WEIGHT = 500;
var SUPER_LIGHT_WEIGHT = 20;
return {
//create brackets from a list of competitors
returnBrackets: function(competitors) {
var brackets = {};
//get super light weights
brackets.superLightWeights = createBracketsByWeightGroup(
filterWeightGroup(competitors, 0, SUPER_LIGHT_WEIGHT)
.sort(compareByWeight)
);
brackets.superHeavyWeights = createBracketsByWeightGroup(
filterWeightGroup(competitors, SUPER_HEAVY_WEIGHT, Infinity)
.sort(compareByWeight)
);
brackets.middleWeights = createBracketsByWeightGroup(
filterWeightGroup(competitors, SUPER_LIGHT_WEIGHT, SUPER_HEAVY_WEIGHT)
.sort(compareByWeight)
);
return brackets;
}
};
}]);
I would like to unit test not just the functions / properties that are exposed in the return statement, but also the functions that are outside of the return statement.
My test is currently set up something like this:
describe('BracketService', function() {
beforeEach(module('bracketManager'));
it('calling return brackets with no competitors will return 3 empty weight classes', inject(function(BracketService) {
var mockCompetitors = [];
var mockBracketResult = {superHeavyWeights: [[]], superLightWeights: [[]], middleWeights: [[]]};
expect(BracketService.returnBrackets(mockCompetitors)).toEqual(mockBracketResult);
}));
});
But how do I test the compare, filter and createBrackets functions that are not exposed by the return statement?
Thanks!
There is no way to test those functions. Their scope is the function that comprises your BracketService factory and they are invisible anyplace else. If you want to test them, then you have to expose them somehow.
You can move them into their own service (which seems like overkill) or you can black box test your BracketService service with enough data combinations to make sure the internal functions are working. That's probably the most sensible approach.
If you don't want to put them in a separate service, but still feel the need to test those internal functions, just return them from the factory along with returnBrackets.
I might do this when I have a number of helper functions that are straight forward to test individually, but open up a combinatorial Pandora's box to black box test. I usually preface such functions with an "_" to show they are helper functions and are only exposed for testing.
return {
//create brackets from a list of competitors
returnBrackets: function(competitors) {...},
_filterWeightGroup: filterWeightGroup,
_createBracketsByWeightGroup: createBracketsByWeightGroup
};
You will not be able to call those functions without exposing them somehow. But, IMHO, private methods should not have a unit test perse, but be tested at the time the public method that calls them is tested. What you should do is mock the objects that your private function will receive and you will be able to perform expectations on them.
The only way to test them in your current setup is to test the returned function since they're currently local to the scope inside the BracketService. If you want them to be individually testable, you'll need to expose them in the return statement as properties of BracketService.

Javascript scope issue, inside an anonymous function

Sorry I couldn't be anymore specific with the title.
I'm building a web-site (personal), which displays different content to the user depending on the query string that is used in the url.
e.g. page=home.html would display home.html
The websites Javascript is wrapped inside an object, with each value containing different data, some pseudo code:
(function(){
var wrapper = {
init: function(){
//Runs on document ready
this.foo();
this.nav.render();
},
foo: function(){
//Some functionality goes here for the website, e.g. Display something from an API
},
nav: {
//Functionality to handle the navigation, has different properties
config: {
//Contains the config for nav, e.g. page names + locations
dir: '/directory/to/content/',
pages: {
page_name: wrapper.nav.config.dir + 'page_value'
}
},
render: function(){
//some code
},
routes: function(){
//some code}
}
}
};
$(function(){
wrapper.init();
});
})();
My problem is that I'm trying to prepend the dir value to each of the page values (inside the object where the pages are defined), expecting to get the output of (in this pseudo code case) of directory/to/content/page_value, but instead dir is undefined when I'm trying to access it, I've tried the following to achieve what I want:
wrapper.nav.config.dir + 'page_value'
I've been playing around with the last 30 minutes trying to find out what I'm doing wrong, and even thought about hard-coding the URL in for each page.
The reasoning for wanting to do this is that my local development server and web host have different directory structures, so I don't want to re-write the URL's each time I want to develop + publish. As for why everything is wrapped inside an object, I thought it would be easier to maintain this way.
Hopefully the answer is simple and it's just an amateur mistake / lack of understanding.
The issue is that you can't refer to a variable that is being defined in that very definition.
So, inside the definition of wrapper, you can't refer to wrapper. And, inside the definition of config, you can't refer to config either and so on.
The usual design pattern for solving this is to initialize as much as you can in the declaration of your data structure and then do the rest in .init() when you can freely access all of it.
Change the first two lines to:
var wrapper = null;
(function(){
wrapper = {
Otherwise, the wrapper is a local variable to your anonymous function.
The problem is that you're still busy defining the wrapper when you ask for its value, which is why it's still undefined.
The code below fails too:
var x = {
y:"1",
z:x.y
}
Why not:
//...
init: function(){
//Runs on document ready
this.foo();
var config = this.nav.config;
for (var page in config.pages) {
config.pages[page] = config.dir + config.pages[page];
}
},
//...

Categories