Jest test fails when using window object environment variable? - javascript

My tests were running fine first, I am now using environmental variables based on the window object like:
{window._env_.REACT_APP_URL}
But when I run my test: it shows as TypeError: Cannot read property 'REACT_APP_URL' of undefined
Do I need to do something different in my test files to accept this type of variable, when I change to {process.env.REACT_APP_URL} everything works as expected?
Any idea's?

The _env_ property does not exist on the window object out of the box, hence TypeScript is complaining that you are assigning a value to an undefined property.
Best way to solve your issue is to reassign the window variable to a custom one that combines the current window types with your own custom EnvironmentVariables interface and use that in your tests instead:
interface EnvironmentVariables {
'_env_': {
'REACT_APP_URL': string
}
}
const customWindow = window as Window & typeof globalThis & EnvironmentVariables;
// set the _env_ object with values
customWindow._env_ = {
'REACT_APP_URL': 'url here'
}
// access REACT_APP_URL
customWindow._env_.REACT_APP_URL

Related

Typescript not triggering errors for an uninitialized object property named "name"

I've found some weird behavior on a Typescript code that I'm writing.
Typescript playground link
Somehow Typescript does not trigger an error for an not initialized property if that property name is name :
What is this? Is this a bug or what?
This is the error for the value property:
No value exists in scope for the shorthand property 'value'. Either declare one or provide an initializer.(18004)
interface INTERFACE_A {
name: string, // ALSO TRIED WITH OTHER TYPES. SAME RESULT
}
function fooA(x: INTERFACE_A): INTERFACE_A { return x; }
fooA({
name // THIS SHOULD BE AN ERROR (BUT IT'S NOT)
});
const objA: INTERFACE_A = {
name // THIS SHOULD BE AN ERROR (BUT IT'S NOT)
}
interface INTERFACE_B {
value: string
}
function fooB(x: INTERFACE_B): INTERFACE_B { return x; }
fooB({
value // THIS IS AN ERROR (OK)
});
const objB: INTERFACE_B = {
value // THIS IS AN ERROR (OK)
}
This doesn't seem to be related to the Typescript Playground, since it's also happening in my VSCode dev environment:
UPDATE:
This is probably related to the window.name property, which is of type string. But it still behaves differently than window.innerWidth, for example:
See that TS complains if I use innerWidth as a string, but it does not complain when I try to use window.name as a number:
Typescript playground link
{ name } is shorthand for { name: name }, window.name is built-in property, and properties of window are global in browsers, so name is a global property that's equivalent to window.name.
In effect, your code is doing this:
fooA({
name: window.name
})
and that's totally valid (although probably not what you wanted).
For a solution to this kind of error, ESLint can be configured to give errors when you use browser globals like name without an explicit reference to the window object (window.name)
Install ESLint
Install ESLint and it for typescript code (instructions here)
install the no-restricted-globals package
Add this to your rules in .eslintrc.js:
module.exports = {
// ... other fields omitted
rules: {
// ... other rules omitted
"no-restricted-globals": ["error"].concat(
require("confusing-browser-globals")
),
},
}

OnChange function not defined when I reference my script as a module? [duplicate]

I have my HTML setup like this:
<script type="module" src="main.js"></script>
and all the ES6 modules work fine. The only problem is I now can't refer to anything from within DevTools (like using the Console and typing in a variable to see it's value or using a function manually).
How do I import modules whilst being able to use the DevTools? Thanks!
One way to make a variable accessable within DevTools is to create it on the window object:
// Variable in your module
var importantNumber = 1;
window.importantNumber = importantNumber;
This method works fine if you just have a couple of variables, but if you need to have access to a lot more variables within DevTools, I would recommend you go to the sources-tab in DevTools, search for your module and adding a breakpoint. When the execution pauses, you have access to all the variables within that module on the DevTools console.
If you want to be able to refer to variables created within the module from the console's global scope, you'll have to deliberately expose each such variable that you want to be visible from the console. Either assign each variable to window (probably not a good idea - the whole point of modules is to make things more modular, without global pollution), or perhaps assign a single object to window, to which you assign module variables. For example:
// in the console:
setTimeout(() => {
window.myModule.foo();
console.log(window.myModule.bar);
});
<script type="module">
function foo() {
console.log('doing foo');
}
const bar = 'a string bar';
const thisModule = { foo, bar };
window.myModule = thisModule;
// If you ever reassign variables, you'll have to reassign them on thisModule too
// or, only reference and reassign properties of thisModule, rather than create independent variables
</script>
For anyone else interested, if you're comfortable with it, use a bundler like Webpack. I don't believe (at least at this point) that the browser will by itself be able to use the DevTools on modules (the other solutions are quite janky, and aren't fantastic to work with).
Hopefully in the future, when all major browsers will be able to support ES6 modules without a bundler, we'll be able to use DevTools.
Using a Helper
I personally use a little helper function in development that allows me to expose a bunch a variables in a single expression. For example, it makes the following two blocks equivalent:
window.playerOne = playerOne;
window.someClass = someClass;
window.localData = localData;
globalize({playerOne, someClass, localData});
The helper looks like this:
const globalize = function(variables) {
Object.entries(variables).forEach(([name, value]) => window[name] = value);
};

interacting with javascript through the chrome console [duplicate]

This question already has answers here:
Console access to Javascript variables local to the $(document).ready function
(8 answers)
Closed 6 years ago.
I'm using a shopping cart api to build an ecommerce website. The creators made an sdk and then you have to make your own .js file for some other functions.
While debugging I would insert a console.log(etc..) anywhere in my .js file so that I could debug object options and etc..
But I would like to be able to use the sdk as a live tool, so instead of having to edit my .js file with new console.log() lines, I'd rather just be able to type object.color_code and have the console output that string for the object color code. At the moment though it just gives me uncaught reference error, object is not defined.
I think this is because my custom .js file has all of it's script inside a $(function() { EVERYTHING }); SO, when I try to call anything in EVERYTHING from the console it says it's undefined, but if I just used console.log inside EVERYTHING it would work. So is there a way I can get around this?
Feel free to explain why it isn't working but I'd like a way to enable this, don't tell me there isn't a way, even if I have to prefix what I want with the .js file it's coming from each time, I don't mind
You were correct in that all of your variables inside the function are only being defined locally, and thus can't be accessed via the console. However, in Javascript there are at least two options for setting global variables from inside functions; If you use these to declare a variable you want to access from outside the function, it will work:
Assign a value to an undeclared variable: varname=value;
Assign the variable to the window object: window.varname=value; or window['varname']=value;
A possible workaround is to expose the object(s) that you want to debug in the global scope:
(function() {
var privateStuff = { foo: 'bar' };
// make privateStuff public for debugging purposes
window['debugObject'] = privateStuff;
})();
document.write(debugObject.foo);
If you want to expose several objects with rather common names that are likely to collide with existing ones, make sure to expose them within an object with an uncommon name rather than directly:
(function() {
var x = { str: 'this is' },
y = { str: 'a test' };
window['debugObject'] = {
x: x,
y: y
};
})();
document.write(debugObject.x.str + ' ' + debugObject.y.str);
If you're happy to change the source file then you could export whatever you want to access from EVERYTHING as a global.
$(function() {
//EVERYTHING
...
window.Ireply = window.Ireply || {};
window.Ireply.object = object;
...
});
console.log(Ireply.object); // some object
You can change a declaration like
$(function(){
var cart = {};
})
To
var cart;
$(function(){
cart = {}
})
Or
$(function(){
var cart = {};
window.cart = cart;
})
But you will want to avoid polluting global namespace. You will also want to be careful about using globals inside callbacks or loops where you can run into unexpected behaviors since local variables scope is often important to be kept local

avoid Javascript variable modification from browser console

I have a problem. I have defined some global variables and namespaced it into an object called "app".
Example:
window.app : {
foo : null,
bar : null,
}
Well, the idea is that I want to be able to modify those variables from any module by calling app.foo = "baz" or app.bar = "baz", but I don't want the user to be able to modify those variables from the browser console (element inspector).
Is it possible?
PD: Well, I have a Backbone.js collection which is sinchronized with the server. I don't want the user to be able to modify that collection with the console
No. The browser is the user's domain. They have the possibility to modify your scripts and inject their own functionality in various ways (through the console or browser plug-ins). That's one of the reasons why you should never blindly trust user input on the server side.
They could even manually forge a complete request, tricking your server into thinking that your JavaScript code made that request.
If you want these values to be secure, you need to keep them on the server. You can send them to the client, of course, as long as you keep a possibility to validate the values against those on the server.
The only way to make the variables not (easily) modifiable by a user is to remove them from global scope - something like
!function() {
foo = null;
bar = null;
}()
You'll need to redesign the way your modules interact with each other to accomplish this. An MVC Framework like Angular.js will help.
You should never rely on this as a security mechanism, though - the browser is fully in the user's control.
Still for them who are searching solution to this problem, use const modifier while assigning variable instead of var. Now try to change value of variable from browser console. It will throw error Uncaught TypeError: Assignment to constant variable that will prevent your data from being modified.
A possible way to avoid to (easily) modify javascript variables from the browser console is to either use the get operator (ECMAScript 5) or a getter-function.
To make it possible to define "private" variables, an anonymous function defines the variables in the local scope, so that it is not globally available. (as mentioned in joews' answer)
As mentioned before, this does not make it impossible to manipulate the variables.
Via get operator:
window.app = (function () {
var _foo = 123; // private variable
return {
get foo () { return _foo; }
};
}());
// --- accessing app from the console ---
// app.foo is readable from console, but not modifiable
console.log(app.foo);
app.foo = 234;
console.log(app.foo); // 123
// However, app.foo can still be modified via Object.defineProperty or
// removed with the delete operator
Via getter-function (older browsers, e.g IE < 9):
window.app = (function () {
var _foo = 123; // private variable
return {
foo: function() { return _foo; }
};
}());
// --- accessing app from the console ---
console.log(app.foo()); // 123
// However, the foo function can still be overwritten.
// But at least, the internal _foo variable is unaffected.
app.foo = function () { return 234; }

ExtJS 4 Object.prototype fail

have a bit problem to use prototype while using framework ExtJS version 4.1.1.
At first I made my prototypings before I load ExtJS.
On "Array.prototype.xyz" and "String.prototype.xyz" all work fine.
Bot on "Object.prototype.xyz" there is a bad behavior in mixin inclusion of ExtJS.
Example my test code:
Object.prototype.doSomething = function() {
console.log('I do it!');
}
var a = {};
a.doSomething();
Error message from ExtJS:
Uncaught TypeError: Cannot read property '$childEls' of undefined
And break.
And:
- Yes. Without "Uncaught TypeError: Cannot read property '$childEls' of undefined" it work
fine.
- No. I use not oter mixins currently.
- Yes. I try to use only one dummy panel Component.
Question: Is there a simple solution to prototype on Object class-object?
The problem stems from one of the fundamental methods of the Ext JS library: Ext.merge
Proving this is very simple:
Object.prototype.doSomething = function(){ console.log("Does something"); };
var emptyObj = {};
console.log(emptyObj.hasOwnProperty("doSomething")); // Prints "false"
var mergeObj = Ext.merge({}, {a: "b"});
console.log(mergeObj.hasOwnProperty("doSomething")); // Prints "true"
Basically, every time Ext.merge (or Ext.apply) is called with an object literal your prototype method is "promoted" up the prototype chain. When you go to create a panel (or any component, really) the class mixin object is merged with its prototype's mixin object. Since a mixin is defined as an object literal in the class definition, your "doSomething" method is promoted.
Then in Ext.util.ElementContainer#getClassChildEls, the mixin object is iterated over assuming each property is an existing class and tries to access mixins[name].self.$childEls (where mixins[name] is your "doSomething" method). Your method doesn't have a self property so accessing $childEls throws the error.
If you need an object available on every object, write it as a static method like Object.doSomething or even Ext.Object.doSomething.

Categories