Ionic - Runtime Error: Object(...) is not a function - node.js

I have this very basic project:
https://github.com/napolev/ionic-start
which is basically the Ionic starter project:
// https://ionicframework.com/getting-started
$ ionic start ionic-start tabs
I added the following two files:
/src/extras/social-media.ts
import * as Promise from 'bluebird';
import { User } from './user';
export var LoginPromise = function(service): Promise<string> {
return new Promise((resolve) => {
setTimeout(function(){
resolve('Will Smith');
}, 2000)
});
};
//*/ IF I COMMENT THIS BLOCK OUT, THERE IS NO ERROR
// this function is not used by this project
// but for some specific reasons it is required to be here
// this code is a simplification of a bigger project
export var callMeIfYouNeedMe = function(id) {
return new Promise((resolve) => {
User.findById(id, function(err, user) {
console.log(user);
resolve(user);
});
});
}
// END OF BLOCK */
/src/extras/user.ts
import { Document, Schema, Model, model} from "mongoose";
export interface IUser {
name: string;
}
export interface IUserDocument extends IUser, Document {
}
export var UserSchema: Schema = new Schema({
name: String,
});
export const User: Model<IUserDocument> = model<IUserDocument>("User", UserSchema);
The code right above works properly on a node project (I tried by myself). I got it from: http://brianflove.com/2016/10/04/typescript-declaring-mongoose-schema-model/#schema--model
With the last export I create a model of type: IUserDocument.
I also modified the following two files:
/src/pages/home/home.ts
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { LoginPromise } from '../../extras/social-media';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public navCtrl: NavController) {
}
test() {
console.log('Hello World');
LoginPromise('facebook').then(
(name: string) => {
console.log('### Social Media -> ' + name);
}
);
}
}
/src/pages/home/home.html
<ion-header>
<ion-navbar>
<ion-title>Home</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h2>Welcome to Ionic!</h2>
<p>
This starter project comes with simple tabs-based layout for apps
that are going to primarily use a Tabbed UI.
</p>
<p>
Take a look at the <code>src/pages/</code> directory to add or change tabs,
update any existing page or create new pages.
</p>
<button ion-button (click)="test()" color="primary">Test</button>
</ion-content>
My problem, is that when I run:
$ ionic serve --no-open
I don't get any error on the console, but on the browser I get the following:
"Runtime Error: Object(...) is not a function"
as you can see here:
Do you have any idea about what could be going on here and how can I make this work?
Thanks!

Runtime Error: Object(…) is not a function
Your call :
model<IUserDocument>("User", UserSchema);
is wrong. model is not a function. Its an object.
Fix
Don't invoke an object. The code is wrong.

Related

next-i18next Jest Testing with useTranslation

Testing libs...always fun. I am using next-i18next within my NextJS project. We are using the useTranslation hook with namespaces.
When I run my test there is a warning:
console.warn
react-i18next:: You will need to pass in an i18next instance by using initReactI18next
> 33 | const { t } = useTranslation(['common', 'account']);
| ^
I have tried the setup from the react-i18next test examples without success. I have tried this suggestion too.
as well as just trying to mock useTranslation without success.
Is there a more straightforward solution to avoid this warning? The test passes FWIW...
test('feature displays error', async () => {
const { findByTestId, findByRole } = render(
<I18nextProvider i18n={i18n}>
<InviteCollectEmails onSubmit={jest.fn()} />
</I18nextProvider>,
{
query: {
orgId: 666,
},
}
);
const submitBtn = await findByRole('button', {
name: 'account:organization.invite.copyLink',
});
fireEvent.click(submitBtn);
await findByTestId('loader');
const alert = await findByRole('alert');
within(alert).getByText('failed attempt');
});
Last, is there a way to have the translated plain text be the outcome, instead of the namespaced: account:account:organization.invite.copyLink?
Use the following snippet before the describe block OR in beforeEach() to mock the needful.
jest.mock("react-i18next", () => ({
useTranslation: () => ({ t: key => key }),
}));
Hope this helps. Peace.
use this for replace render function.
import { render, screen } from '#testing-library/react'
import DarkModeToggleBtn from '../../components/layout/DarkModeToggleBtn'
import { appWithTranslation } from 'next-i18next'
import { NextRouter } from 'next/router'
jest.mock('react-i18next', () => ({
I18nextProvider: jest.fn(),
__esmodule: true,
}))
const createProps = (locale = 'en', router: Partial<NextRouter> = {}) => ({
pageProps: {
_nextI18Next: {
initialLocale: locale,
userConfig: {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
},
},
} as any,
router: {
locale: locale,
route: '/',
...router,
},
} as any)
const Component = appWithTranslation(() => <DarkModeToggleBtn />)
const defaultRenderProps = createProps()
const renderComponent = (props = defaultRenderProps) => render(
<Component {...props} />
)
describe('', () => {
it('', () => {
renderComponent()
expect(screen.getByRole("button")).toHaveTextContent("")
})
})
I used a little bit more sophisticated approach than mocking to ensure all the functions work the same both in testing and production environment.
First, I create a testing environment:
// testing/env.ts
import i18next, { i18n } from "i18next";
import JSDomEnvironment from "jest-environment-jsdom";
import { initReactI18next } from "react-i18next";
declare global {
var i18nInstance: i18n;
}
export default class extends JSDomEnvironment {
async setup() {
await super.setup();
/* The important part start */
const i18nInstance = i18next.createInstance();
await i18nInstance.use(initReactI18next).init({
lng: "cimode",
resources: {},
});
this.global.i18nInstance = i18nInstance;
/* The important part end */
}
}
I add this environment in jest.config.ts:
// jest.config.ts
export default {
// ...
testEnvironment: "testing/env.ts",
};
Sample component:
// component.tsx
import { useTranslation } from "next-i18next";
export const Component = () => {
const { t } = useTranslation();
return <div>{t('foo')}</div>
}
And later on I use it in tests:
// component.test.tsx
import { setI18n } from "react-i18next";
import { create, act, ReactTestRenderer } from "react-test-renderer";
import { Component } from "./component";
it("renders Component", () => {
/* The important part start */
setI18n(global.i18nInstance);
/* The important part end */
let root: ReactTestRenderer;
act(() => {
root = create(<Component />);
});
expect(root.toJSON()).toMatchSnapshot();
});
I figured out how to make the tests work with an instance of i18next using the renderHook function and the useTranslation hook from react-i18next based on the previous answers and some research.
This is the Home component I wanted to test:
import { useTranslation } from 'next-i18next';
const Home = () => {
const { t } = useTranslation("");
return (
<main>
<div>
<h1> {t("welcome", {ns: 'home'})}</h1>
</div>
</main>
)
};
export default Home;
First, we need to create a setup file for jest so we can start an i18n instance and import the translations to the configuration. test/setup.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import homeES from '#/public/locales/es/home.json';
import homeEN from '#/public/locales/en/home.json';
i18n.use(initReactI18next).init({
lng: "es",
resources: {
en: {
home: homeEN,
},
es: {
home: homeES,
}
},
fallbackLng: "es",
debug: false,
});
export default i18n;
Then we add the setup file to our jest.config.js:
setupFilesAfterEnv: ["<rootDir>/test/setup.ts"]
Now we can try our tests using the I18nextProvider and the useTranslation hook:
import '#testing-library/jest-dom/extend-expect';
import { cleanup, render, renderHook } from '#testing-library/react';
import { act } from 'react-dom/test-utils';
import { I18nextProvider, useTranslation } from 'react-i18next';
import Home from '.';
describe("Index page", (): void => {
afterEach(cleanup);
it("should render properly in Spanish", (): void => {
const t = renderHook(() => useTranslation());
const component = render(
<I18nextProvider i18n={t.result.current.i18n}>
<Home / >
</I18nextProvider>
);
expect(component.getByText("Bienvenido a Pocky")).toBeInTheDocument();
});
it("should render properly in English", (): void => {
const t = renderHook(() => useTranslation());
act(() => {
t.result.current.i18n.changeLanguage("en");
});
const component = render(
<I18nextProvider i18n={t.result.current.i18n}>
<Home/>
</I18nextProvider>
);
expect(component.getByText("Welcome to Pocky")).toBeInTheDocument();
});
});
Here we used the I18nextProvider and send the i18n instance using the useTranslation hook. after that the translations were loaded without problems in the Home component.
We can also change the selected language running the changeLanguage() function and test the other translations.

Jest error "SyntaxError: Need to install with `app.use` function" when using vue-i18n plugin for Vue3

I am using vue-i18n plugin for my Vue3(typescript) application. Below is my setup function in component code
Home.vue
import {useI18n} from 'vue-i18n'
setup() {
const {t} = useI18n()
return {
t
}
}
Main.ts
import { createI18n } from 'vue-i18n'
import en from './assets/translations/english.json'
import dutch from './assets/translations/dutch.json'
// internationalization configurations
const i18n = createI18n({
messages: {
en: en,
dutch: dutch
},
fallbackLocale: 'en',
locale: 'en'
})
// Create app
const app = createApp(App)
app.use(store)
app.use(router)
app.use(i18n)
app.mount('#app')
Code works and compiles fine. But jest test cases fails for the component when it's mounting
Spec file
import { mount, VueWrapper } from '#vue/test-utils'
import Home from '#/views/Home.vue'
import Threat from '#/components/Threat.vue'
// Test case for Threats Component
let wrapper: VueWrapper<any>
beforeEach(() => {
wrapper = mount(Home)
// eslint-disable-next-line #typescript-eslint/no-empty-function
jest.spyOn(console, 'warn').mockImplementation(() => { });
});
describe('Home.vue', () => {
//child component 'Home' existance check
it("Check Home component exists in Threats", () => {
expect(wrapper.findComponent(Home).exists()).toBe(true)
})
// Threat level list existance check
it("Check all 5 threat levels are listed", () => {
expect(wrapper.findAll('.threat-level .level-wrapper label')).toHaveLength(5)
})
})
Below is the error
Please help me to resolve this.
The vue-18n plugin should be installed on the wrapper during mount with the global.plugins option:
import { mount } from '#vue/test-utils'
import { createI18n } from 'vue-i18n'
import Home from '#/components/Home.vue'
describe('Home.vue', () => {
it('i18n', () => {
const i18n = createI18n({
// vue-i18n options here ...
})
const wrapper = mount(Home, {
global: {
plugins: [i18n]
}
})
expect(wrapper.vm.t).toBeTruthy()
})
})
GitHub demo
You can also define the plugin globally in the setup/init file:
import { config } from '#vue/test-utils'
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
// vue-i18n options here ...
})
config.global.plugins = [i18n]
config.global.mocks.$t = (key) => key

Cannot redirect to new page on first submission of form with history.push()

Edit
I've done some more debugging and here is the problem:
CreateProfile.js calls profileActions.createProfile() and passes data to be operated on and this.props.history so that it can push a new path onto the history stack.
profileActions.createProfile() successfully sends data to database. Database successfully uses the data.
profileActions.createProfile() pushes new path onto stack. The component at the path loads and successfully calls a reducer.
The URL in the browser does not reflect the path that is pushed onto the history stack. The new component does not load.
This only happens when creating an entry in the database. When updating an entry, the program works as expected.
I'm currently trying to redirect to a new page with react/redux. On the first submission, the form submits to the backend and creates an entry in the database but fails to redirect to the next page. On the second submission, however, it redirects just fine.
I'm using this.props.history.push() to do the redirect.
I think It may be an issue with the the response received from the backend but I cannot seem to figure out what the issue is. The reason I believe this is because it is hitting different logic because on the second submission, it is updating and not creating an entry.
Here is my component (CreateProfile.js)
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { createProfile } from '../../actions/profileActions';
import TextAreaGroup from '../common/TextAreaGroup';
import InputGroup from '../common/InputGroup';
class CreateProfile extends Component {
// Constructor
// componentWillRecieveProps()
onSubmit = (evt) => {
evt.preventDefault();
const profileData = {
handle: this.state.handle,
bio: this.state.bio,
website: this.state.website,
twitter: this.state.twitter,
instagram: this.state.instagram,
youtube: this.state.youtube,
linkedin: this.state.linkedin,
github: this.state.github,
vsco: this.state.vsco
};
this.props.createProfile(profileData, this.props.history);
}
//onChange()
render() {
// render logic
return (
// markup
<form onSubmit={this.onSubmit}>
// markup
<input
type="submit"
value="Create Profile"
className="btn btn-info btn-block mt-4"
/>
</form>
</div>
</div>
</div>
</div>
)
}
}
CreateProfile.propTypes = {
createProfile: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
profile: state.profile,
errors: state.errors
});
export default connect(mapStateToProps, { createProfile })(withRouter(CreateProfile));
Here is my action file that submits to the backend (profileActions.js):
import axios from 'axios';
// import types
import { GET_PROFILE, PROFILE_LOADING, GET_ERRORS, CLEAR_CURRENT_PROFILE } from './types';
// Create Profile
export const createProfile = (profileData, history) => dispatch => {
axios.post('/api/profile', profileData)
.then(res => history.push('/login'))
.catch(err => {
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
})
};
}
And here is the route in my backend that is being submitted to:
router.post('/', passport.authenticate('jwt', { session: false }), (req, res) => {
const { errors, isValid } = validateProfileInputs(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
const profileFields = {}; //code setting fields omitted
Profile.findOne({user: req.user.id}).then(profile => {
if (profile) {
// Update Profile
Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFields },
{ new: true }
).then(profile => res.json(profile)); // SUCCESSFUL PUSH ONTO THIS.PROPS.HISTORY
} else {
// Create Profile
// Check if handle exists
Profile.findOne({ handle: profileFields.handle })
.then(profile => {
if (profile) {
errors.handle = 'That handle already exists';
res.status(400).json(errors);
}
new Profile(profileFields).save().then(profile => res.json(profile)); // PUSH ONTO THIS.PROPS.HISTORY NOT OCCURRING
});
}
});
});
Any and all help would be greatly appreciated. I have tried my hardest but cannot seem to figure out what the issue is.
This problem arose because of my lack of understanding of how asynchronous javascript works.
The issue was with a few lines of code in the component that I was trying to push too.
componentDidMount() {
this.props.getProfile(); // Async function, sets profile object in store
}
render() {
const { profile } = this.state.profile;
if(!Object.keys(profile).length > 0) { // This is always evaluates to true
// because it executes before
// this.props.getProfile() returns
this.props.history.push('/create-profile');
}
}

How to pass the data from one component to another component using angular5

I am creating test application using angular so in that i need to display user details while i click on edit button then, that user details will need to display in another component.i have written query to get user details while clicking edit button, but unable to get data so what is the exact procedure for changes need to be done in query.
This is my manageusers.component.html
<tr *ngFor="let detail of userDetails" style="text-align:center">
<td><input type="checkbox"></td>
<td>{{detail.username}}</td>
<td>{{detail.uemail}}</td>
<td>Inactive</td>
<td>{{detail.added_on}}</td>
<td>
<a routerLink="/dashboard-info/basic-settings">
<i class="fas fa-edit" (click)="editIssue(i,detail._id)"></i>
</a>
This is my manageusers.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup , Validators } from '#angular/forms';
import { DataService } from '../../../services/data.service';
import { AccountService } from '../../../services/account.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-manage-users',
templateUrl: './manage-users.component.html',
styleUrls: ['./manage-users.component.css'],
})
export class ManageUsersComponent implements OnInit {
userDetails:any = [];
detail:Detail={
added_on:'',
username:'',
uemail:'',
upassword:'',
};
constructor(private router: Router,private fb:FormBuilder,private dataService: DataService, private accountService: AccountService) {}
editIssue(id,detail){
alert(detail);
let data = {
_id:detail,
};
this.accountService.editDetail(data).subscribe(
response => {
console.log(response);
this.userDetails = JSON.parse(response);
//this.router.navigate(['/dashboard-info/basic-settings', this.userDetails]);
},
err => {
console.error('User Not found');
})
}
ngOnInit() {}
}
interface Detail{
added_on:string
username:string,
upassword:string,
uemail:string,
}
accountService.ts
editDetail(data) {//Getting User with userId
return this.http.post(this.apiPath + 'user/editDetail', data,{responseType: 'text'});
}
usercontroller.js
userRouter.post('/editDetail', function (req, res) {
console.log(req.body._id);
Collections.user.findOne({_id: req.body._id}, function (err, result) {
if (err) return res.status(500).send("There was a problem finding the user");
if (result) {
console.log(result);
res.status(200).send(result);
} else {
return res.status(500).send("User Not Found with Details: " + JSON.stringify(user));
}
});
});
I think it would be better to set the User you want to show in the component as input, get the User you need in an http call and pass it to the component afterwards by it's input. See more for component input here: https://toddmotto.com/passing-data-angular-2-components-input
To retrieve data by http call you should use Angulars Http Client, which is really easy to use and saves you from using plain javascript. See here: https://blog.angular-university.io/angular-http/

How to use connection as standalone object with types?

Not working code just to illustrate what I'm trying to achieve
Some connection file
import { ConnectionManager } from 'typeorm';
const c = new ConnectionManager();
// user ormconfig.conf file
export const connection = c.createAndConnect();
using in some model
#Entity()
#Table("annual_incomes")
export class AnnualIncome
{
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 75 })
variant: string;
#Column("int")
sort: number;
#Column()
is_active: boolean;
}
Later somewhere in the code, I want to get connection with all methods, something like:
import { connection } from 'someconnection';
import { AnnualIncome } from 'entities';
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await connection.getRepository(AnnualIncome).find();
});
Usually, I'm getting an error from tsc that .getRepository() method was not found in connection. However if I do something like:
import { connection } from 'someconnection';
import { AnnualIncome } from 'entities';
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await connection.then(async connection => {
return await connection.getRepository(AnnualIncome).find();
}
});
the above code works with definitions and tsc does not complain about not-existing methods.
I'd like to avoid an extra definition connection.then() and get plain connection with all methods defined in <Connection> type.
just use createConnection method to create your connection when you bootstrap your application. Later you can access your connection from anywhere using getConnection() method:
import { AnnualIncome } from 'entities';
import { createConnection, getConnection } from 'typeorm';
// somewhere in your app, better where you bootstrap express and other things
createConnection(); // read config from ormconfig.json or pass them here
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await getConnection().getRepository(AnnualIncome).find();
});
Also you can simply use getRepository method also avalible from anywhere:
import { AnnualIncome } from 'entities';
import { getRepository } from 'typeorm';
// some code here
api.get('/incomes', async (ctx) => {
ctx.body = await getRepository(AnnualIncome).find();
});

Resources