How to mock a property (path.sep) in Jest - properties

I need to test if a module works both under Windows and Linux. The module is resolving a path and depending on the OS the path contains a forward slash or backslash. It uses the path.sep to determine which slash to use. In my test case I want to test both OS's. My problem is I cannot figure out how to change the value of path.sep as it is a property and not a function.
Am I missing something? How can I mock the path.sep property? I hope someone can shred some light on this.

Here is a simple working example:
code.js
const path = require('path');
export const func = () => path.sep === '/' ? 'foo' : 'bar';
code.test.js
const path = require('path');
import { func } from './code';
test('func', () => {
path.sep = '/';
expect(func()).toBe('foo'); // Success!
path.sep = '\\';
expect(func()).toBe('bar'); // Success!
});
Looks like it also works if you use
import path from 'path';
...but it doesn't work (at least in my environment) with
import * as path from 'path';
(Since path isn't currently an ES6 module the transpiler has to do some work to make the ES6 import syntax work and import * as path from 'path'; must get transpiled in a way that breaks the approach above.)

Related

Dynamic import of component based on variable name in NextJS

I'm looking for a way to use a component in a dynamic way:
const getDynamicComponent = (componentName) => dynamic(() => import(${componentName}), {
ssr: false,
loading: () => <p>Loading...</p>,
});
const Test = () => {
const router = useRouter();
const { component } = router.query;
const DynamicComponent = getDynamicComponent(component);
return <DynamicComponent />
}
Obiovusly if I specify a folder name there like components/${componentName} it searches ALL components.. and not the one specified in the variable.
I know the documentation states explicitly that template strings are not an option for dynamic imports, but I would like to know how I would be able to import a specific component from node_modules, without importing all node_modules folder 🙂
Only static strings are permitted?
I have a dynamic route file as [componentId].js which should import its component from node_modules based on the route name.. any ideas?
Should I try to configure babel in a specific way to make this work?
Thanks!
PS: I know it was asked here, but those answers are not quite correct as the whole folder is trying to get imported, not just the component specified.

"Cannot use import statement outside a module", even as mjs

I have been trying to import a simple function that says "Blah Blah" to my express server.
Unfortunately I am getting the error: SyntaxError: Cannot use import statement outside a module.
I have tried to google this issue the whole day, tried to change the file to .mjs and more, but the server keeps crashing with this error regardless of the methods I found online.
ExpressServer.js :
//start express server
const express = require("express");
const server = express();
const path = require("path");
var bodyParser = require("body-parser");
//Require functions that saves to DB
import {saveToDB} from './saveToDB.js';
// parse application/x-www-form-urlencoded
server.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
server.use(bodyParser.json());
server.listen(5000, () => {
console.log("express server is running..");
});
//Send form details to DB
server.get("/save", (req, res) => {
phone = req.body.number;
name = req.body.name;
saveToDB();
});
saveToDB.js
const saveToDB = () => {
console.log(`Blah blah blah`);
};
export default saveToDB;
The error:
import {saveToDB} from ('./saveToDB.js');
^^^^^^
SyntaxError: Cannot use import statement outside a module
First, check what Node version you are using. If it's 13 or above, ES6 and thus the import statement is supported. Else, you have to update your node version or use the ES5 way (require) instead of import.
Second: If the version is 13 or above, you can either change your file extension to .mjs or add { "type": "module" } to you package.json file.
Third: Always put your import statements at the top of the file. Also, I would probably not mix require with import, but that's personal preference.
Problem was fixed, apparently the problem starts when you try to use import on server side instead of require. If you still wish to use import you can update your node version and edit the type in package.json.
If you choose to go with the first route, you will also have to change all the "Require" in your file to import, otherwise you will get "ReferenceError: require is not defined"
The easier solution would be to change:
import {saveToDB} from './saveToDB.js';
With
const saveToDB = require("./saveToDB.js");
And in the exported module, use the older exporting method:
module.exports = saveToDB;

Vue CLI dynamic import from project module

I know it is possible to imports component from a bundled webpack file like this
import {componentA, componentB} from '../component.bundle';
Is it possible to achieve this same as a dynamic import, like this.
const url = '${window.location.origin}/assets/default/js/bundle/vue_data.bundle.js';
this.getAxiosInstance().get(url).then(response => {
const {Add, Edit, Filter, View} = () => import(response.data);
});
If yes, please how? Thank you
Yes, using dynamic imports:
To dynamically import a module, the import keyword may be called as a function. When used this way, it returns a promise.
I'm assuming from your url example that your import is in the assets directory of your project (i.e. src > assets) and not actually on an external domain. In that case, you don't need axios. You would do it like this:
const path = 'assets/default/js/bundle/vue_data.bundle.js';
import('#/' + path).then(module => {
const {Add, Edit, Filter, View} = module;
});
You need to hardcode the #/ portion or there will be issues, e.g. if you tried to do import(path) with const path = '#/assets/default/js/bundle/vue_data.bundle.js'

React Native Expo Environment Variables

So I'm happy with the concept of environment variables as explained in this article and others
https://www.freecodecamp.org/news/how-to-gracefully-use-environment-variables-in-a-react-native-app/
Great, I've got my SOMETHING="something" stored so I can just use env.SOMETHING or whatever
The part I'm a little lost on is where you keep the live variables?
I would rather not do a solution like this as it seems you are still keeping your keys quite public and that you are just choosing based on the environment with if statements
Manage environment with expo react native
For example with an Express App deployment we have, we specify
let endPointURL = env.endPointURL
and then we keep a versoin of that variable locally and when it sits with AWS it is overridden by AWS servers as explained here
I was just wondering does something like that exist for Android and iOS builds (on the respective stores) or through Expo?
Thanks all
Honestly I think the way they go about it is a little silly. There may be a better way to go about than this, but I think I followed their documentation suggestions.
https://docs.expo.io/versions/latest/distribution/release-channels/#using-release-channels-for-environment-variable-configuration
They have a code snippet suggesting you create a function to look at the release configuration itself.
I interpreted it that you might do something like the code below and store your environment variables in a variables.js file and pull in your environment variables as such.
import Constants from 'expo-constants';
export const prodUrl = "https://someapp.herokuapp.com";
const ENV = {
dev: {
apiUrl: "http://localhost:3000"
},
staging: {
apiUrl: prodUrl
},
prod: {
apiUrl: prodUrl
}
};
function getEnvVars(env = "") {
if (env === null || env === undefined || env === "") return ENV.dev;
if (env.indexOf("dev") !== -1) return ENV.dev;
if (env.indexOf("staging") !== -1) return ENV.staging;
if (env.indexOf("prod") !== -1) return ENV.prod;
}
export default getEnvVars(Constants.manifest.releaseChannel);
Edit:
Now that Expo supports config file as app.config.js or app.config.ts, we can use the dotenv. Check this: https://docs.expo.io/guides/environment-variables/#using-a-dotenv-file
A simpler approach would be to export the env object instead of the function:
import Constants from 'expo-constants';
import { Platform } from "react-native";
const localhost =
Platform.OS === "ios" ? "localhost:8080" : "10.0.2.2:8080";
const ENV = {
dev: {
apiUrl: localhost,
amplitudeApiKey: null,
},
staging: {
apiUrl: "[your.staging.api.here]",
amplitudeApiKey: "[Enter your key here]",
// Add other keys you want here
},
prod: {
apiUrl: "[your.production.api.here]",
amplitudeApiKey: "[Enter your key here]",
// Add other keys you want here
}
};
const getEnvVars = (env = Constants.manifest.releaseChannel) => {
if (env === null || env === undefined || env === "" || env.indexOf("dev") !== -1) return ENV.dev;
if (env.indexOf("staging") !== -1) return ENV.staging;
if (env.indexOf("prod") !== -1) return ENV.prod;
}
const selectedENV = getEnvVars();
export default selectedENV;
// Import
import env from '..xxx/utility/env';
Get it in your ios-generated file, based on .env file:
In .env, write down GOOGLE_MAPS_API=abcde...
yarn add react-native-config
cd ios
pod install
In your Objective-C-compiled code, for example, AppDelegate.m:
#import "ReactNativeConfig.h"
NSString *mapsApiKey = [ReactNativeConfig envFor:#"GOOGLE_MAPS_API"];
[GMSServices provideAPIKey:mapsApiKey];
Credits to: ReactNative: Pass JS variable to AppDelegate based on https://github.com/luggit/react-native-config.
Android should work as well, but haven't tested / followed https://github.com/luggit/react-native-config.
Edit: required steps for Android:
<meta-data android:name="com.google.android.geo.API_KEY" android:value="#string/GOOGLE_MAPS_API"/> in AndroidManifest.xml.
In settings.gradle:
include ':react-native-config'
project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android')
right after rootProject.name = 'XYZ'
In build.gradle:
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" right below import com.android.build.OutputFile and implementation project(':react-native-config') right below implementation "com.facebook.react:react-native:+" // From node_modules
Regarding "normal" usage in .ts, .tsx, .js files I'm declaring variables in .env based on https://github.com/goatandsheep/react-native-dotenv by declaring "module:react-native-dotenv" in babel.config.js in plugins, and it works like a charm like so:
import { ACCESS_TOKEN } from "#env";
...
headers: {
Authorization: `Bearer ${ACCESS_TOKEN}`,
Accept: "application/json",
},
Edit: important the eas build ignores .gitignore-declared variable, so if your .env is in .gitignore the production bundle won't have it included.
Surprised there weren't any answers that involved storing environment variables in a .env file with Expo.
I prefer storing my environment variables in a .env file because I don't want to commit certain variables to version control and hardwire them into my application code.
Create your .env file and add your environment variables
Install dotenv
npm install dotenv
In your app.config.js file, load the environment variables from the .env file via dotenv:
require("dotenv").config();
export default {
// config...
}
Expose the environment variables to the Expo runtime in the config in app.config.js:
require("dotenv").config();
export default {
// rest of config...
extra: {
ENV_VAR: process.env.ENV_VAR
}
}
Now you can access your environment variables through the following:
import Constants from "expo-constants";
const ENV_VAR = Constants.expoConfig.extra.ENV_VAR
OPTIONAL: TypeScript
To make using environment variables in our code a little nicer, lets create a typed helper utility to access the environment variables:
import Constants from "expo-constants";
export interface Env {
ENV_VAR: string;
}
/**
* Environment variables exposed through `app.config.js`
* An environment variable not there? Make sure it is explicitly defined in `app.config.js`
*/
export const env = Constants.expoConfig?.extra as Env;
Then you can simply access your environment variables from the env object:
const ENV_VAR = env.ENV_VAR
OPTIONAL: Throw an error if an environment variable is not set
This can be handy to prevent your app from running if an environment variable required for your app to properly function is not set.
In your app.config.js:
// Validate all necessary environment variables are set
const checkForEnvVariable = (envVar) => {
if (!process.env[envVar]) {
throw Error(`${envVar} not set! Check env.md for more information`);
}
};
[
"ENV_VAR",
// ...
].forEach((envVar) => checkForEnvVariable(envVar));

React native require relative path file

I am trying React Native iOS and I was trying to setup project environments. I create a file called config.js so later I can just
import config from 'env'
to load the variables based on different environments. The following is the config.js file
let configFile = 'dev.js'
if (NODE_ENV === 'production') {
configFile = 'prod.js'
}
export default require('./env/' + configFile)
Somehow this won't work. the error message is:
Requiring unknown module "./env/dev.js". If you are sure the module is there, try restarting the packager or running "npm install"
When I changed my code to the following it would not give me errors. But it is not what I wanted to do.
export default require('./env/dev.js')
So does anyone know why is that?
Firstly, require calls are not dynamic. They are statically analyzed and bundled. So you would want something like this
let prodConfig = require('./env/prod.js');
let devConfig = require('./env/dev.js');
let config;
if (process.env.NODE_ENV === 'production') {
config = prodConfig;
}else {
config = devConfig;
}
export default config;