Problem calling mongoose service with nestjs in jest testing - mongodb

Currently i'm trying to test my service but always fails and prints the error, even when the app is running correctly and the service is working
Error
TypeError: undefined is not a function
at Array.find (<anonymous>)
at NewsService.findAll (MY_ROUTE\src\news\news.service.ts:28:8)
at Object.it (MY_ROUTE\src\news\news.service.spec.ts:33:10)
at Object.asyncJestTest (MY_ROUTE\node_modules\jest-jasmine2\build\jasmineAsyncInstall.js:106:37)
at resolve (MY_ROUTE\node_modules\jest-jasmine2\build\queueRunner.js:45:12)
at new Promise (<anonymous>)
at mapper (MY_ROUTE\node_modules\jest-jasmine2\build\queueRunner.js:28:19)
at promise.then (MY_ROUTE\node_modules\jest-jasmine2\build\queueRunner.js:75:41)
at process._tickCallback (internal/process/next_tick.js:68:7)
at service.findAll.then.catch (news/news.service.spec.ts:39:19)
news.service.spec.ts
import { NewsService } from './news.service';
import { Model } from 'mongoose';
import { News, NewsSchema } from './schemas/news.schema';
import { getModelToken } from '#nestjs/mongoose';
describe('NewsService', () => {
let service: NewsService;
const mockRepository = (...args: any[]) => { };
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [NewsService, { provide: getModelToken(News.name), useFactory: mockRepository }],
}).compile();
service = module.get<NewsService>(NewsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('get news', () => {
it('should get all news', async () => {
service
.findAll()
.then((allNews) => {
console.log(allNews);
expect(allNews).toBeDefined();
})
.catch((error) => {
console.log(error);
});
});
});
});
news.schema.ts
import { Document } from 'mongoose';
export type NewsDocument = News & Document;
#Schema()
export class News extends Document {
#Prop({ unique: true })
id: string;
#Prop()
title: string;
#Prop()
date: string;
}
export const NewsSchema = SchemaFactory.createForClass(News);
news.service.ts
import { Model } from 'mongoose';
import { InjectModel } from '#nestjs/mongoose';
import { News } from './schemas/news.schema';
#Injectable()
export class NewsService {
constructor(
#InjectModel(News.name) private readonly newsModel: Model<News>
) {}
public async findAll(): Promise<News[]> {
return await this.newsModel
.find()
.sort([['date', 'descending']])
.exec();
}
}
I'm just learning about Jest, but after a lot of research and tests, i couldn't figured out what i'm doing wrong exactly.
EDIT
This is the only "decent" thing that i've tried to, but other errors appear. Maybe my whole focus on this is wrong.
const mockRepository = (...args: any[]) => {
findAll: jest.fn().mockReturnValue([
new News({
id: '1',
title: 'title',
date: 'date',
}),
]);
};
Error
TypeError: Cannot read property 'plugin' of undefined
5 |
6 | #Schema()
> 7 | export class News extends Document {
| ^
8 | #Prop({ unique: true })
9 | id: string;
10 |
at News.Object.<anonymous>.Document.$__setSchema (../node_modules/mongoose/lib/document.js:3028:10)
at new Document (../node_modules/mongoose/lib/document.js:86:10)
at new News (news/schemas/news.schema.ts:7:1)
at InstanceWrapper.mockRepository [as metatype] (news/news.service.spec.ts:13:7)
at Injector.instantiateClass (../node_modules/#nestjs/core/injector/injector.js:293:55)
at callback (../node_modules/#nestjs/core/injector/injector.js:77:41)

Related

Mongoose Schema properties validation with Typescript NextJS

i am trying to save new document to mongo db, the Schema validation is not working for me, i am trying ti make required true, but i still can add new document without the required field.
this is my schema:
// lib/models/test.model.ts
import { Model, Schema } from 'mongoose';
import createModel from '../createModel';
interface ITest {
first_name: string;
last_name: string;
}
type TestModel = Model<ITest, {}>;
const testSchema = new Schema<ITest, TestModel>({
first_name: {
type: String,
required: [true, 'Required first name'],
},
last_name: {
type: String,
required: true,
},
});
const Test = createModel<ITest, TestModel>('tests', testSchema);
module.exports = Test;
this is createModel:
// lib/createModel.ts
import { Model, model, Schema } from 'mongoose';
// Simple Generic Function for reusability
// Feel free to modify however you like
export default function createModel<T, TModel = Model<T>>(
modelName: string,
schema: Schema<T>
): TModel {
let createdModel: TModel;
if (process.env.NODE_ENV === 'development') {
// In development mode, use a global variable so that the value
// is preserved across module reloads caused by HMR (Hot Module Replacement).
// #ts-ignore
if (!global[modelName]) {
createdModel = model<T, TModel>(modelName, schema);
// #ts-ignore
global[modelName] = createdModel;
}
// #ts-ignore
createdModel = global[modelName];
} else {
// In production mode, it's best to not use a global variable.
createdModel = model<T, TModel>(modelName, schema);
}
return createdModel;
}
and this is my tests file:
import { connection } from 'mongoose';
import type { NextApiRequest, NextApiResponse } from 'next';
const Test = require('../../../lib/models/test.model');
import { connect } from '../../../lib/dbConnect';
const ObjectId = require('mongodb').ObjectId;
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
switch (req.method) {
case 'POST': {
return addPost(req, res);
}
}
}
async function addPost(req: NextApiRequest, res: NextApiResponse) {
try {
connect();
// const { first_name, last_name } = req.body;
const test = new Test({
first_name: req.body.first_name,
last_name: req.body.last_name,
});
let post = await test.save();
// return the posts
return res.json({
message: JSON.parse(JSON.stringify(post)),
success: true,
});
// Erase test data after use
//connection.db.dropCollection(testModel.collection.collectionName);
} catch (err) {
//res.status(400).json(err);
res.status(400).json({
message: err,
success: false,
});
}
}
in the Postman, i send a request body without the required field (first_name) and i still can add it.
any help?

Mongo Memory Server: Property 'getUri' does not exist on type '(opts?: MongoMemoryServerOpts) => Promise<MongoMemoryServer>'

I am trying to run e2e tests on a nestjs app.
I have trouble running MongoMemoryServer, in order to run set the MMS I used this article based on the original nestJs documentation.
I keep getting this error:
test/user-preferences.e2e-spec.ts:27:32 - error TS2339: Property 'getUri' does not exist on type '(opts?: MongoMemoryServerOpts) => Promise<MongoMemoryServer>'.
27 const uri = mongod.getUri();
~~~~~~
Test Suites: 1 failed, 1 total
This is the test I try to run:
import { Test, TestingModule } from '#nestjs/testing';
import { getModelToken, MongooseModule } from '#nestjs/mongoose';
import { MongoMemoryServer } from 'mongodb-memory-server';
import {
UserPreferences,
UserPreferencesDocument,
UserPreferencesSchema,
} from './../src/user-preferences/schemas/user-preferences.schema';
import { UserPreferencesModule } from './../src/user-preferences/user-preferences.module';
import * as request from 'supertest';
import { factory } from 'fakingoose';
import { Model } from 'mongoose';
describe('userPreferences controller', () => {
let userPreferencesModel;
let app;
const UserPreferencesFactory = factory<UserPreferencesDocument>(
UserPreferencesSchema,
).setGlobalObjectIdOptions({ tostring: false });
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
await MongooseModule.forRootAsync({
useFactory: async () => {
const mongod = await MongoMemoryServer.create;
const uri = mongod.getUri();
return {
uri: uri,
};
},
}),
UserPreferencesModule,
],
}).compile();
app = moduleFixture.createNestApplication();
console.log('app: ', app);
userPreferencesModel = moduleFixture.get<Model<UserPreferencesDocument>>(
getModelToken(UserPreferences.name),
);
await app.init();
});
beforeEach(() => {
// populate the DB with 1 UserPreference using fakingoose
const mockUserPreferences = UserPreferencesFactory.generate();
return userPreferencesModel.create(mockUserPreferences);
});
afterEach(() => userPreferencesModel.remove({}));
it('GET /api/v1/user-preferences', () => {
return request(app.getHttpServer())
.get('/api/v1/user-preferences')
.expect(200)
.expect((res) => {
console.log('res: ', res);
expect(res.body.length > 0).toBe(true);
});
});
afterAll(() => {
app.close();
});
});
This is my schema file:
import { Schema, Prop, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export enum exitToOptions {
THIS_POST = 'this_post',
ALL_POSTS = 'all_posts',
DASHBOARD = 'dashboard',
}
export type UserPreferencesDocument = UserPreferences & Document;
#Schema()
export class UserPreferences {
#Prop({ unique: true })
eUserId: string;
#Prop()
uiTheme: string;
#Prop()
panelWidth: number;
#Prop()
editingHandles: boolean;
#Prop()
enableLightboxInEditor: boolean;
#Prop()
hiddenElements: boolean;
#Prop()
defaultDeviceView: string;
// #Prop()
// exitTo: exitToOptions
#Prop()
exitTo: string;
}
export const UserPreferencesSchema =
SchemaFactory.createForClass(UserPreferences);

React/Meteor Component not passing Props properly

new to Meteor and running into this issue. I am using Meteor 1.3.3
When I try to pass props from my parent Container to my React Component it keeps throwing an error I will post below.
Here is my React component Prospect.jsx:
import React from 'react'
import { createContainer } from 'meteor/react-meteor-data'
import { Residents } from '/collections/residents.jsx'
import ReactDOM from 'react-dom';
import RaisedButton from 'material-ui/RaisedButton';
// import '/collections/residents.jsx'
class Prospect extends React.Component {
render() {
return(
<div>
<h1>Prospect Resident - {this.props.prospect.name.first} </h1>
<RaisedButton label="Default" />
</div>
)
}
}
Prospect.propTypes = {
// prospect: React.PropTypes.object
}
export default createContainer((params) => {
const paramsId = params.params.prospectId
Meteor.subscribe('residents');
// Meteor.subscribe('resident');
prospect = Residents.find({_id: paramsId}).fetch()
console.log(prospect[0])
return {
prospect: prospect
}
}, Prospect)
and here is my Mongo collection
residents.jsx
import { Mongo } from 'meteor/mongo'
export const Residents = new Mongo.Collection('residents')
const nameSchema = new SimpleSchema({
first: {type: String},
last: {type: String}
})
const residentSchema = new SimpleSchema({
cId: { type: String },
name: { type: nameSchema },
status: { type: String },
})
Residents.attachSchema(residentSchema)
// METHODS
Meteor.methods({
'residents.insert'(resident) {
Residents.insert(resident)
}
})
// PUBLICATIONS
if(Meteor.isServer) {
Meteor.publish('residents', function() {
return Residents.find()
})
Meteor.publish('resident', function(id) {
return Residents.find({_id: id})
})
}
and here is my Route
FlowRouter.route('/prospects/:prospectId}', {
name: 'prospectShow',
action(params) {
mount(LoggedIn, { content:
<MuiThemeProvider muiTheme={getMuiTheme()}>
<Prospect params={{prospectId: params.prospectId}} />
</MuiThemeProvider>
})
}
So when I go to localhost:3000 route I get the error
Prospect.jsx:14Uncaught TypeError: Cannot read property 'name' of undefined
Exception from Tracker recompute function:
debug.js:41 TypeError: Cannot read property '_currentElement' of null
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:772)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:753)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:672)
at ReactCompositeComponentWrapper.receiveComponent (ReactCompositeComponent.js:571)
at Object.receiveComponent (ReactReconciler.js:127)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:775)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:753)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:672)
at ReactCompositeComponentWrapper.performUpdateIfNecessary (ReactCompositeComponent.js:585)
at Object.performUpdateIfNecessary (ReactReconciler.js:160)
My console.log(prospect[0]) in the container returns the object just fine, and it also works if I pass it in like this
return {
prospect: {name: {first: 'Joe', last: 'Smith'}}
}
So it's something about the returned object I think. Any help would be greatly appreciated, thanks
I ended up going with a solution like this. If anyone wants to answer and explain why this is needed (I thought in meteor 1.3 this wasn't needed anymore) I will accept your answer.
import React from 'react'
import { createContainer } from 'meteor/react-meteor-data'
import { Residents } from '/collections/residents.jsx'
class Prospect extends React.Component {
render() {
if(!this.props.ready){return <span>Loading...</span>}
const { prospect } = this.props
return(
<div>
<h1>{prospect.name.first} {prospect.name.last}</h1>
<div>Company: <b>{prospect.cId}</b></div>
</div>
)
}
}
Prospect.propTypes = {
ready: React.PropTypes.bool.isRequired,
prospect: React.PropTypes.object.isRequired
}
export default createContainer((params) => {
return {
ready: Meteor.subscribe('resident', params.id).ready(),
prospect: Residents.findOne(params.id)
}
}, Prospect)

AngularJS 2 Typescript interface

I have a service for handling users operations and an interface for the user object.
user.service.ts
import {Injectable} from 'angular2/core';
export interface User {
name: string;
email?: string;
picture?: string;
}
#Injectable()
export class UserService {
me: User;
constructor() {
}
setUser(user: User) {
this.me = user;
}
}
In my login component I try to set the user with the profile returned from the login service but I get this error:
Property 'firstName' does not exist on type '{}'.
login.component.ts
import {Component} from 'angular2/core';
import {User, UserService} from './services/user.service';
import {LinkedinService} from './services/linkedin.service';
declare const IN: any;
console.log('`Login` component loaded asynchronously');
#Component({
selector: 'Login',
providers: [
UserService,
LinkedinService
],
template: require('./login.html')
})
export class LoginComponent {
me: User;
constructor(public linkedinService: LinkedinService, public userService: UserService) {
this.me = userService.me;
}
ngOnInit() {
console.log('hello `Login` component');
}
login() {
this.linkedinService.login()
.then(() => this.linkedinService.getMe()
.then(profile => this.userService.setUser({ name: profile.firstName })));
}
}
linkedin.service.ts
import {Injectable} from 'angular2/core';
declare const IN: any;
#Injectable()
export class LinkedinService {
constructor() {
IN.init({
api_key: 'xxxxxxxxxxx',
authorize: true
});
}
login() {
return new Promise((resolve, reject) => {
IN.User.authorize(() => resolve());
});
}
getMe() {
return new Promise((resolve, reject) => {
IN.API.Profile('me').result((profile) => resolve(profile.values[0]));
});
}
}
I'm trying to import the User interface from UserService and use inside the LoginComponent but I don't know what I'm doing wrong. Any idea? I am not sure if I have to use the User interface inside the LoginComponent, is that right?
Narrow in on the code :
.then(() => this.linkedinService.getMe())
.then(profile => this.userService.setUser({ name: profile.firstName })));
The type of profile is driven by the response of this.linkedinService.getMe(). Seems like it is something like Promise<{}>. It does not have the member firstName. Hence the error:
Property 'firstName' does not exist on type '{}'.
Fix
Check to the code / signatures of linkedinService. This has nothing to do with the user.service.ts file that the question contains 🌹
Update
Focus in on the code:
getMe() {
return new Promise((resolve, reject) => {
IN.API.Profile('me').result((profile) => resolve(profile.values[0]));
});
}
The value returned is driven by what is being passed to resolve. So make sure profile.values[0] has the right type. Alternatively provide the hint to the compiler:
getMe() {
return new Promise<{firstName:string}>((resolve, reject) => {
IN.API.Profile('me').result((profile) => resolve(profile.values[0]));
});
}

Ionic 2 HTTP request not working - Angular 2

Hi I'm trying to do a simple Http GET request, but can't get it to work in ionic v2 Beta...
here is my app.js:
import {App, Platform} from 'ionic-angular';
import {TabsPage} from './pages/tabs/tabs';
import {HTTP_BINDINGS} from 'angular2/http';
#App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
providers: [HTTP_BINDINGS],
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
static get parameters() {
return [[Platform]];
}
constructor(platform) {
this.rootPage = TabsPage;
platform.ready().then(() => {
});
}
}
and this is my page1.js:
import {Page} from 'ionic-angular';
import {Http} from 'angular2/http';
#Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {
constructor(http:Http) {
this.mget = http.get("https://httpbin.org/ip")
.subscribe(data => {
var alert = Alert.create({
title: "Your IP Address",
subTitle: data.json().origin,
buttons: ["close"]
});
this.nav.present(alert);
}, error => {
console.log(JSON.stringify(error.json()));
});
}
}
When adding http:Http to the constructor -> constructor(http:Http) the whole app goes blank in browser...
And I get an error in Console:
Error: Cannot find module "../page1/page1"
I've also tried this in Page1.js:
export class Page1 {
constructor() {
}
makeGetRequest() {
this.http.get("https://httpbin.org/ip")
.subscribe(data => {
var alert = Alert.create({
title: "Your IP Address",
subTitle: data.json().origin,
buttons: ["close"]
});
this.nav.present(alert);
}, error => {
console.log(JSON.stringify(error.json()));
console.log('yolo')
alert('hello');
});
}
}
and then call makeGetRequest() on (click) in page1.html
but it returns these exeptions:
EXCEPTION: Error during evaluation of "click"
ORIGINAL EXCEPTION: TypeError: this.http is undefined
please help!
:)
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
THIS IS THE SOLUTION:
page1.js:
import {Page} from 'ionic-angular';
import {Http} from 'angular2/http';
#Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {
static get parameters(){
return [Http];
}
constructor(http) {
this.http = http;
this.mget = this.http.get("https://httpbin.org/ip")
.subscribe(data => {
console.log(data);
}, error => {
console.log('faild');
});
}
}
app.js:
import {App, Platform} from 'ionic-angular';
import {TabsPage} from './pages/tabs/tabs';
import { HTTP_PROVIDERS } from 'angular2/http';
#App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
providers: [HTTP_PROVIDERS],
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
static get parameters() {
return [[Platform]];
}
constructor(platform) {
this.rootPage = TabsPage;
platform.ready().then(() => {
});
}
}
Please try this
export class Page1 {
static get parameters(){
return [Http];
}
constructor(http) {
this.http = http;
this.mget = this.http.get("https://httpbin.org/ip")
.subscribe(data => {
var alert = Alert.create({
title: "Your IP Address",
subTitle: data.json().origin,
buttons: ["close"]
});
this.nav.present(alert);
}, error => {
console.log(JSON.stringify(error.json()));
});
}
}
I would recommend you to write the get request inside a separate service and inject it in your page.
Also have a look at this - http://tphangout.com/?p=113
Detailed and simple instructions are given there for making a simple GET request from an Ionic 2 app.
I believe you need to
import { HTTP_PROVIDERS } from 'angular2/http';
in your app.js instead of HTTP_BINDINGS and change providers: [HTTP_BINDINGS] to providers: [HTTP_PROVIDERS]
See Angular2 docs