I have a util module consisting of the following namespaced exports
// utils.js
export const getWindowLocation(){window.location}
export const func2(){getWindowLocation().href = ....}
I would like to stub/override getWindowLocation in my mocha test
//UtilTest.js
import * as utils from "src/js/utils/utils";
import sinon from "sinon";
describe("Utils", () => {
beforeEach(() => {
sinon.stub(utils, "getWindowLocation").returns({});
});
....
The stub seems not to be overriding my method. I am not sure if namespaced function should be treated differently?
Thank you,
Related
I just meet a problem that could not load the CSS inside react function component with material UI. Though I fixed it, I am still curious about the reason.
In the beginning, I write something like this, where I put both makeStyles and useStyles inside the function component. Then, I find that the class name is correctly assigned to the element, but no css is loaded.
import makeStyles from "material-UI"
import styles from "styles.js"
export default function alertPage() {
const useStyles = makeStyles(styles);
const classes = useStyles();
const [alert, setalert] = React.useState();
const showAlert = () => {
setalert(<p className={classes.text}></p>)
}
return (
<button onClick={showAlert}></button>
{alert}
)
}
Then, I put makeStyles outside the function, everything works correctly.
import makeStyles from "material-UI"
import styles from "styles.js"
const useStyles = makeStyles(styles);
export default function alertPage() {
const classes = useStyles();
const [alert, setalert] = React.useState();
const showAlert = () => {
setalert(<p className={classes.text}></p>)
}
return (
<button onClick={showAlert}></button>
{alert}
)
}
Then, I tried this code putting both inside function component again, but return the HTML directly, which still works.
import makeStyles from "material-UI"
import styles from "styles.js"
export default function alertPage() {
const useStyles = makeStyles(styles);
const classes = useStyles();
return (
<p className={classes.text}></p>
)
}
Then, I check the react official site and find that they always put CSS outside the function component, but not getting any sentences mentioned that this is required or why.
My guess is that I misunderstand the scope of the const or how CSS is actually loaded in the browser. If anybody could tell me the reason, or which piece of knowledge I missed, like js, ts, react, or how the browser works?
Thanks!
From the Material UI docs, you should have something like the below:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
root: {
// Your styles
},
});
export default function Hook() {
const classes = useStyles();
// Rest of your code
}
If you have styles in a correctly formatted import, you should be able to add them directly in the makeStyles declaration:
const useStyles = makeStyles(styles)
I am currently trying to unit test a container that pulls in a static JSON file of phone numbers and passes it to the component to display, however I am not sure how I should go about testing it. The code for the container is as follows:
import React from 'react';
import data from *JSON file location*
import CountryInfo from *component for the country information* ;
class CountryInfoContainer extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
numbersJson: null
};
}
async componentWillMount() {
const numbersJson = data;
this.setState({ numbersJson });
}
render() {
return (
<CountryInfo json={this.state.numbersJson} showText={this.props.showText} />
);
}
}
export default CountryInfoContainer;
I currently have my unit test to look like this
import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import { mount, configure } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
import CountryInfoContainer from './CountryInfoContainer';
configure({ adapter: new Adapter() });
describe('Successful flows', () => {
test('checks if json has null entries', () => {
const wrapper = mount(<MemoryRouter><CountryInfoContainer /></MemoryRouter >);
const data = wrapper.find(numbersJson);
// eslint-disable-next-line no-console
console.log(data.debug);
});
});
Obviously, it doesn't work now because I am not sure how to use the variable numbersJson in the container in the test file or how to check if it is null.
The variable numbersJson is not defined in the scope of your test. If I understand correctly, you are testing that when you first mount the component, that it's state contains a null value for the numbersJson key.
First of all, you need to mount your component directly without MemoryRouter:
const wrapper = mount(<CountryInfoContainer />);
Then you can write an expect() for the state:
expect(wrapper.state().numbersJson).toBeNull();
I'm testing for state within a Create-React-App with Enzyme. How can I pass this test?
When my App component is rendered in my test it is wrapped in
<BrowserRouter>
(attempting to mount it otherwise yields a
Invariant failed: You should not use <Route> outside a <Router>
error in the test).
Shallow wrapping yields
TypeError: Cannot read property 'state' of null
as does mounting and wrapping with
<BrowserRouter>.
I have tried this
Result: Question unanswered
this
Result: Question unanswered
this
Result: Uninstalling react-test-renderer made no difference
and this
Result: I checked the state in my component and it is defined.
console.log(wrapper.instance().state) yields the same error: 'null'
App.js:
class App extends Component {
//#region Constructor
constructor(props) {
super(props);
this.state = {
//... other correctly formatted state variables
specificRankingOptionBtns: false
}
app.test.js:
import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import App from '../../App'
import renderer from 'react-test-renderer'
import { shallow, mount } from 'enzyme';
describe('App', () => {
fit('renders App.js state correctly', () => {
const wrapper = mount(<BrowserRouter><App /></BrowserRouter>);
//console.log(wrapper.instance().state);
//const wrapper = shallow(<App />);
//const wrapper = mount(<App />);
console.log(wrapper.instance().state);
//const wrapper = mount(shallow(<BrowserRouter><App />
//</BrowserRouter>).get(0));
expect(wrapper.state('specificRankingOptionBtns')).toEqual(false);
});
}
Expect: test to pass
Actual: "TypeError: ReactWrapper::state("specificRankingOptionBtns") requires that state not be null or undefined"
I had the same issue. This worked for me.
import { MemoryRouter as Router } from 'react-router-dom';
it('should find MaskedTextInput in LoginPage', () => {
const mountWithRouter = node => mount(<Router>{node}</Router>);
const wrapper = mountWithRouter(<LoginPage {...mockedProps} />);
const maskedInput = wrapper.find('MaskedTextInput');
const componentInstance = wrapper
.childAt(0)
.childAt(0)
.instance(); // could also be instance
const mountedState = componentInstance.state.passwordInputType;
expect(mountedState).toEqual('password');
});
I have a file for configuring my OpenID Connect authentication
export const authMgr = new Oidc.UserManager({
userStore: new Oidc.WebStorageStateStore(),
authority: **appsetting.oidc**
})
I want to access my state in order to get the value of appsetting.
I did this:
import store from './store'
const appsetting = () => store.getters.appsetting
but my appsetting is always returning undefined
what I my missing?
Store:
app.js
const state = {
appsetting: appsetting,
}
export {
state
}
getters.js
const appsetting = state => state.appsetting
export {
appsetting
}
index.js
export default new Vuex.Store({
actions,
getters,
modules: {
app
},
strict: debug,
plugins: [createLogger]
})
when I print the value of store.getters, it returns this:
{
return __WEBPACK_IMPORTED_MODULE_2__store__["a" /* default */].getters;
}
No the actual store objects
Try to import 'store' with curly brackets
import {store} from '../store/index'
store.getters.appSettings
Another option is to access from the vue property
import Vue from 'vue'
Vue.store.getters.appSettings
As for 2023 accesing store with
import {store} from '../store/index'
store.getters.appSettings
wasnt working for me but after removing curly brackets like so:
import store from '../store/index'
store.getters.appSettings
It started working as intended
I want to load Constant File in Angular 2(which is a Normal TypeScript File) having WebAPI EndPoints.
In Angular1.x. we used to have constants for the same.
How in Angular 2 I can Implement the Same?
I have created the .ts file.My main concern lies in how to load the file beforehand every Other class File loads.
.ts file :
export class testAPI {
getAPI = "myUrl";
}
In service file I am using the same by doing Normal Import:
constructor(private http: Http) {
//console.log(this.test);
console.log(this.testing.getAPI);
//this.test.load();
}
I am getting the Console as Undefined.(Must be because my Service class is loading before API Class).
Thanks in Advance.
UPDATES
Inspired with the solution for this particular problem created ngx-envconfig package and published it on NPM registery. It has the same functionalities as it is provided in this answer and even more.
You can have the JSON file somewhere in assets folder like: assets/config. Depending on whether the environment is dev or not you can use two .json files, one for development and one for production. So you can have development.json and production.json files, where each one will keep the appropriate API endpoints.
Basically you need to go through the following steps:
1. Setting up environment (skip this step if you have it already)
Create two files in src/environments folder:
environment.prod.ts
export const environment = {
production: true
};
environment.ts
export const environment = {
production: false
};
2. Create JSON config files
assets/config/production.json
{
"debugging": false,
"API_ENDPOINTS": {
"USER": "api/v1/user",
...
}
}
assets/config/development.json
{
"debugging": true,
"API_ENDPOINTS": {
"USER": "api/v1/user",
...
}
}
3. Create a service as follows
Note depending on the environment, the ConfigService will load the appropriate file
import { Injectable, APP_INITIALIZER } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs';
import { environment } from 'environments/environment'; //path to your environment files
#Injectable()
export class ConfigService {
private _config: Object
private _env: string;
constructor(private _http: Http) { }
load() {
return new Promise((resolve, reject) => {
this._env = 'development';
if (environment.production)
this._env = 'production';
console.log(this._env)
this._http.get('./assets/config/' + this._env + '.json')
.map(res => res.json())
.subscribe((data) => {
this._config = data;
resolve(true);
},
(error: any) => {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
});
});
}
// Is app in the development mode?
isDevmode() {
return this._env === 'development';
}
// Gets API route based on the provided key
getApi(key: string): string {
return this._config["API_ENDPOINTS"][key];
}
// Gets a value of specified property in the configuration file
get(key: any) {
return this._config[key];
}
}
export function ConfigFactory(config: ConfigService) {
return () => config.load();
}
export function init() {
return {
provide: APP_INITIALIZER,
useFactory: ConfigFactory,
deps: [ConfigService],
multi: true
}
}
const ConfigModule = {
init: init
}
export { ConfigModule };
4. Integrate with app.module.ts
import { NgModule } from '#angular/core';
import { ConfigModule, ConfigService } from './config/config.service';
#NgModule({
imports: [
...
],
providers: [
...
ConfigService,
ConfigModule.init(),
...
]
})
export class AppModule { }
Now you can use ConfigService wherever you want get the necessary API endpoints defined in config .json files.
In Angular 4+ projects generated with the Angular CLI, you will have the environment folder out-of-the-box. Inside of it, you will find the environment.ts files from Karlen's answer. That is a working solution for configuration with one caveat: Your environment variables are captured at build time.
Why does that matter?
When you're setting up a CI/CD pipeline for your Angular app, you will generally have a build tool that builds your project (like Jenkins) and a deployment tool (like Octopus) that will grab that package (the dist folder) and deploy to the selected environment, replacing your environment variables with the correct values in the process. If you use the environment.ts files, your environment variables cannot be replaced this way because the environment.ts files do not get included in the dist folder. There is no file your deployment tool can pick up and edit.
What can we do? we can add a JSON configuration file inside of the assets folder. Those files are included by default in the dist folder we will want to deploy. When we want to use an environment variable, we simply import the settings like import config from '[relative/path/to/your/config/file.json]'.
When we do this, we will get something like the following error:
Cannot find module '../../config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension
This is because the typescript compiler tries to import an exported module and cannot find one. We can fix this by adding the following JSON properties/values in our tsconfig.json file.
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
resolveJsonModule allows the typescript compiler to import, extract types from, and generate .json files.
allowSyntheticDefaultImports allows default imports from modules with no default export.
With this in place, we can run our project and we will find that our error is gone and we can use our config values without any issues.
Now, because this config file is included in the dist folder that gets deployed on the server, we can configure our deployment tool to replace the variable values with the values specific to the environment to which we want to deploy. With this in place we can build our Angular app once and deploy it anywhere.
Another added benefit is that most deployment tools like Octopus ship with native JSON support so you can configure it to replace environment variables in your JSON file quite easily. The alternative is using a regex solution to replace environment variables in a .ts file, which is comparatively more complicated and prone to mistakes.
It is possible to import JSON in TypeScript. You need to add typings:
typings.d.ts:
declare module "*.json" {
const value: any;
export default value;
}
And then import like this:
import config from "../config/config.json";
config.json:
{
"api_url": "http://localhost/dev"
}
I had same issue and in the end i give up from .ts and put it in .js :D like this:
configuration.js in root
var configuration = {
'apiHost': 'http://localhost:8900',
'enableInMemoryWebApi': false,
'authMode': 'standalone',
'wsUrl': 'ws://localhost:8900/ws'
};
module.exports = configuration;
in .ts file for ex. user.service.ts
let configuration = require('../configuration'); //in import section
#Injectable()
export class UserService {
...
getUser(id: number | string): Promise<User> {
console.log(configuration.apiHost) //will get propertye from .js file
return this.http.get(`${configuration.apiHost}/${id}`, this.headers).toPromise().then(this.extractData).catch(this.handleError);
}
}
Hope it helps
You can use Opague token to set constant values as providers
Try:
In your const file:
import { OpaqueToken } from '#angular/core';
export const CONFIG_TOKEN = new OpaqueToken('config');
export const CONFIG = {
apiUrl: 'myUrl'
};
In your AppModule set to make it a singleton provider for the app:
providers:[
//other providers,
{provide: CONFIG_TOKEN, useValue: CONFIG}
]
For injecting in constructor,
constructor( #Inject(CONFIG_TOKEN) private config)