Global variables in Karma test runner - javascript

I have a global variable defined in my main template, which I use to store information bits from the back end, such as the environment context path. I can't move that variable inside a service.
How can I expose that variable to Karma when I run the unit tests?

You either declare that global variable within your test file:
var global = "something";
describe('Your test suit', function() {
...
});
or add a Javascript file where it's defined to your karma.conf.js file:
// list of files / patterns to load in the browser
files: [
...,
'file-containing-the-global-variable.js'
],

If you are coming from Angular 2+ the only way I found that works is to create the variable or object globally using window:
Google Recapthca Loaded from a script:
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
In this example a call to the google Recaptcha will create a global variable called grecaptcha.
In our component we decide to render a new Google Recaptcha. Of course since we do not declare grecaptcha we need to use declare var to say hey I promise it is declared somewhere:
declare var grecaptcha;
private CreateGoogleCaptcha() {
grecaptcha.render('recaptcha', {
sitekey: this.siteKey,
callback: this.GoogleCaptchaCallback,
badge: 'inline'
});
}
private GoogleCaptchaCallback(token) {
// Handle Callback Logic
}
Now that our function works we decide to run a test but of course we would like to mock our grecaptcha as we don't have control over it.
So we name our global variable we would like to create and add the functions we would like:
beforeEach(() => {
fixture = TestBed.createComponent(GoogleRecaptchaComponent);
component = fixture.componentInstance;
window['grecaptcha'] = {
render() {
console.log('mocked global variable and function');
}
}
}
Update:
Creating the global variable in the beforeEach is fine but what about when it has some sort of callback function such as above that calls a function from your component? Easy enough we just move the login to our test and in our mock we set it to our components function:
it('should ', () => {
window['grecaptcha'] = {
render: function() { GoogleRecaptchaComponent['GoogleCaptchaCallback']('token'); }
};
spyOn<any>(GoogleRecaptchaComponent, 'GoogleCaptchaCallback');
GoogleRecaptchaComponent['CreateGoogleCaptcha']();
expect(GoogleRecaptchaComponent['GoogleCaptchaCallback']).toHaveBeenCalled();
});
Note: spyOn<any>: The <any> is used so we can refence without error the function because it is private, otherwise we will get a typescript error;

The first solution didn't work for me in Angular 2.1.x. It simply would not recognize the variable in my imported service. What I had to do was put my environment variable in my karma-test-shim.js file and remove var so it would be globally available.
Mine looks like this:
Error.stackTraceLimit = Infinity;
require('core-js/es6');
require('reflect-metadata');
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy'),
require('zone.js/dist/sync-test'),
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
// Add environment variables here so that tests will inject them in source code
API_URL = 'http://localhost:8080/api/';
var appContext = require.context('../src', true, /\.spec\.ts/);
appContext.keys().forEach(appContext);
var testing = require('#angular/core/testing');
var browser = require('#angular/platform-browser-dynamic/testing');
testing.TestBed.initTestEnvironment(
browser.BrowserDynamicTestingModule,
browser.platformBrowserDynamicTesting()
);

I was trying to integrate angular v13 with google-place-autocomplete without any npm package, just vanilla google-place-autocomplete. When i tried to test, karma said: '"google" is not defined'.
So i found the awswers above, and it worked for me like this:
Created a file in root directory (in my case, i named: google-place.js)
Added a mock object with the instructions that is used in component.
class Autocomplete {
constructor(input, options) {
const addListener = (eventName, callback) => { }
const getPlace = () => { }
return { addListener, getPlace }
}
}
var google = {
maps: {
places: {
Autocomplete: Autocomplete
}
}
};
Added in karma.conf.js a property called "files", and end up like this:
files: [
"./google-place.js"
]
And it was not necessary to add a declare var google in every spec. Is global now.

Related

How to set environment variables like test environment for playwright-cucumber js

I've been using Playwright with Cucumber for e2e-automation of a react web application. This repo has been my starting point and it's been working out pretty good.
However, I'm looking for pointers on how to run these tests on different test environments - like development or QA, so that the target urls and other params vary as per the environment passed. For eg -
if (env == dev){
baseurl = dev_url
}
else{
baseurl = qa_url
}
The Cucumber documentation mentions the World parameter - an this issue looks like a similar issue, however I'm skeptical of passing a different JSON for this task.
Can this be achieved only at a Cucumber level or is there a Playwright or Node way of doing this?
As you are already using cucumber, define your world file like this:
First you can segregate your properties files into: properties-dev.json and properties-qa.json. Below code reads properties file based on env we are passing in defaultOptions object and stores entire properties file data into 'this'. Use 'this' in your hooks file and call this.keyNameForUrl to get url specific to environment.
Note: 'this' can be accessible only in world and hooks files (refer // https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/world.md). If you need this data in other files, create a separate class and declare all public static varibles in it. In Hooks BeforeAll function, reassign values from 'this' to the static variables created in the class.
import { setWorldConstructor } from 'cucumber';
const fs = require('fs');
const defaultOptions = {
env: 'qa'
};
function processOptions(options) {
let envFile = 'properties-' + options.env + '.json';
let environment = JSON.parse(fs.readFileSync(envFile));
return Object.assign(options, environment);
}
function World(input) {
this.World = input;
});
Object.assign(this, processOptions(Object.assign(defaultOptions, options)), options);
}
setWorldConstructor(World);
// https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/world.md

How to a define a global constant from inside a function Javascript?

I am building an application composed of a set of modules that get loaded when the application bootstrap:
const MODULES = []
function registerModules(config) {
return modules.map(module => (new module(config)).install());
}
function bootstrap() {
MODULES = registerModules({...});
}
The above of-course will raise an error. What I want to do is to assign the MODULE constant only when the app start but then it should be fixed. Is this possible?
Initialisation of MODULES has to happen inside bootstrap because of the config variable that I have to pass to registerModules.
If indeed you want to:
Have an initialised, but empty MODULES array during the time that bootstrap() is not yet called, and
Make MODULES immutable once it has been populated, and
Keep the current function signature of registerModules unchanged;
Then you could do it as follows:
function bootstrap() {
Object.freeze(Object.assign(MODULES, registerModules({...})));
// OR:
// MODULES.push(...registerModules({...}));
// Object.freeze(MODULES);
}
If you don't insist on the existence of MODULES before bootstrap() is called, and you are open to store that information inside an object, then you could proceed as follows:
const globals = {};
function bootstrap() {
globals.MODULES = registerModules({...});
Object.freeze(globals.MODULES);
}
Once you are happy with globals, you can also freeze that object:
Object.freeze(globals);

Include global functions in Vue.js

In my Vue.js application I want to have some global functions. For example a callApi() function which I can call every time I need access to my data.
What is the best way to include these functions so I can access it in all my components?
Should I create a file functions.js and include it in my main.js?
Should I create a Mixin and include it in my main.js?
Is there a better option?
I have a file with function like a func.js
like below
export const func = {
functionName: (data) => {
return something
}
}
In main.js add 2 string
import {func} from './func.js'
Vue.prototype.$func = func
and you can use from all components if in script tag like below
this.$func.functionName(somedata)
or if template tag like
$func.functionName(somedata)
Your best bet would be a Plugin, which lets you add features to the global vue system.
[from the vuejs Docs]
MyPlugin.install = function (Vue, options) {
// 1. add global method or property
Vue.myGlobalMethod = ...
// 2. add a global asset
Vue.directive('my-directive', {})
// 3. add an instance method
Vue.prototype.$myMethod = ...
}
Then you would just add
Vue.use(MyPlugin)
in your code before calling your function.
Vue.myGlobalMethod(parameters);
or in your case
Vue.callApi(parameters);
Mixins can be registered globally​ too. https://v2.vuejs.org/v2/guide/mixins.html#Global-Mixin

Mocha - Accessing named functions with ti-mocha

I'm using a hybrid version of mocha, ti-mocha, to build unit test for a Titanium SDK-based app. I'm completely new to BDD and mocha, so the API learning curve is quite steep. Here's my issue with a stripped-down test harness. I can access the index.js module, its methods and properties. However, I don't know how to access the named functions, in this case doClick(), within that module.
I'm using mocha + should.js. In the test harness below, context "index" passes, but context "doClick" fails. I'm so new to this API, that I'm not sure if I even framed the question properly. How do I access the functions within the module?
index-mocha.js
// creates the "mocha" global necessary to run a test suite anywhere in your app
var should = require('should');
module.exports = function(index) {
// create the test suite
describe('mochatest', function() {
context('index', function() {
it('index exists', function() {
should.exist(index);
});
it('index.open function', function() {
should(index.open).be.a.Function;
});
it('id = index', function() {
index.id.should.equal('index');
});
});
context('doClick', function() {
it('doClick exists', function() {
should.exist(index.doClick);
// return;
});
it('doClick is a function', function() {
should(index.doClick).be.a.Function;
});
});
});
var outputFile = Ti.Filesystem.getFile(Ti.Filesystem.tempDirectory, 'results.json');
outputFile.createFile();
mocha.setup({
reporter: 'ti-spec', // the reporter to use with your tests
outputFile: outputFile, // write results to the given Ti.Filesystem.File file
quiet: false // if true, suppress all console logging
});
// run the tests
mocha.run();
};
index.js
function doClick(e) {
alert($.label.text);
}
if(runTests){
require('ti-mocha');
$.index.addEventListener('open', function(){
require('index-mocha')($.index);
});
}
$.index.open();
Nice to see someone playing around with testing in Titanium :)
Just to clarify a thing, the variable $ refers to an instance of your current controller. Also, Alloy gives you references to view elements for which you have defined an id via this variable; This may be seen as a little sugar as all those views are accessible through $.getViews().
Therefore, all functions defined inside your controller file are accessible only from within that controller. If you want them to be accessible from the outside, the easiest and cleanest way is to exports them.
This can be done in two ways:
By directly add them to the controller object
$.doClick = doClick;
By using the exports variable
exports.doClick = doClick;
The result will be exactly the same as, during the compilation, Alloy will merge the exports variable (which is, initially, only an empty object) with your controller a.k.a $.
Then, just pass your controller through your require instead of the index view, to have access to both views and newly added listeners.

Working with global window variable in mocha js from node

I am new to js unit testing and I am trying to use mocha for my backbone contact manager tutorial that i found at this github repo. However, i have a global window.ContactManager variable that I firsted wanted to test whether it exists and then test the router.on functionality inside the start function later. The variable looks like so:
window.ContactManager = {
Models: {},
Collections: {},
Views: {},
start: function(data) {
var contacts = new ContactManager.Collections.Contacts(data.contacts),
router = new ContactManager.Router();
router.on('route:home', function() {
router.navigate('contacts', {
trigger: true,
replace: true
});
});
router.on('route:showContacts', function() {
var contactsView = new ContactManager.Views.Contacts({
collection: contacts
});
.....
My test that does not work:
var expect = require ('chai').expect;
describe("Application", function() {
it('creates a global variable for the name space ContactManager' , function () {
expect(ContactManager).to.exist;
})
});
How do I test and access a global window variable existence in mocha from running the tests in the console?
You are ignoring the difference between running JavaScript code in the browser and running JavaScript code in Node.
In the browser, the window name is a reference to the object which holds all your global variables. So when you do foo = 1 in the outermost scope, you declare a global foo, which is also accessible as window.foo. Conversely, if you assign a new field like this: window.bar = 1, then you have a new global called bar.
In Node, your global object is accessed as global. So if you do foo = 1 in the outermost scope, foo is also accessible as global.foo. And if you do global.bar = 1, you have a new global named bar.
Your code shows that you modify a window object, which does not appear to be a reference to the global object. Options:
Run Mocha in the browser instead of in Node. See Mocha's documentation.
Set your Node environment so that it mimics enough of a browser environment to satisfy node. Setting a global window variable to be a equal to global might be enough but I don't know Backbone enough to know whether Backbone will be happy with this.
Run your Backbone-based code in jsdom. Jsdom provides realistic window and document, as if your code was running in a browser, but it has its limits. I don't know whether Backbone would be happy with those limits.
Another solution would be to use https://www.npmjs.com/package/window-or-global
import React, { Component } from 'react'
// in node, you'll get the global object instead of crashing by an error
import root from 'window-or-global'
class MyComponent extends Component {
// this method is only invoked in the browser environment
componentDidMount() {
root.addEventListener(/*...*/)
}
componentWillUnmount() {
root.addEventListener(/*...*/)
}
render() {}
}
// Voilà. Enjoy your universal react component! ;)
// No more 'window is not defined' errors when you render your component
// on server side.
To install, run npm install --save window-or-global.
Running tests on server (for example with mocha-webpack) is way more faster than in a browser.

Categories

Resources