I am trying to run my test script using mocha but there is an error of "Reference Error: beforeEach is not defined" - javascript

Mocha test Suite gives a reference error and it says "beforeEach is not defined"
I am trying to run my test script for my todo app in node.js using mocha. But there is a reference error and it says "beforeEach is not defined"
const {app} = require('./../server');
const {Todo} = require('./../models/todo');
beforeEach((done) => {
Todo.remove({}).then(() => done());
});
describe('POST /todos', () => {
it('should create a new todo', (done) => {
var text = 'Test todo text';
request(app)
.post('/todos')
.send({text})
.expect(200)
.expect((res) => {
expect(res.body.text).toBe(text);
})
.end((err, res) => {
if (err) {
return done(err);
}
Todo.find().then((todos) => {
expect(todos.length).toBe(1);
expect(todos[0].text).toBe(text);
done();
}).catch((e) => done(e));
});
});
it('should not create todo with invalid body data', (done) => {
request(app)
.post('/todos')
.send({})
.expect(400)
.end((err, res) => {
if (err) {
return done(err);
}
Todo.find().then((todos) => {
expect(todos.length).toBe(0);
done();
}).catch((e) => done(e));
});
});
});
Also, I have included all the necessary packages for my package.json file.
My Package.json file is given below
{
"name": "todo-api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "mocha server/**/*.test.js",
"test-watch": "nodemon --exec 'npm test' "
},
"author": "",
"license": "ISC",
"dependencies": {
"bluebird": "^3.5.3",
"body-parser": "^1.15.2",
"express": "^4.14.0",
"mongodb": "^2.2.5",
"mongoose": "^4.5.9"
},
"devDependencies": {
"expect": "^1.20.2",
"mocha": "^3.0.2",
"nodemon": "^1.10.2",
"supertest": "^2.0.0"
}
}

It looks to me like you are trying to clear out your data in your MongoDB database before each new test of your todo app. So you want to find your collection of todo and drop everything in there before your next test, if so
beforeEach(() => {
mongoose.connection.collections.todo
});
With the above you are directly referencing the collection of todo sitting inside your database and you call the drop() function on your collection of todo.
beforeEach(() => {
mongoose.connection.collections.todo.drop();
});
Please let me know if I am correct in fulfilling your requirement. Keep in mind this is an asynchronous operation, so you need to ensure you pause the entire testing environment until the operation is complete, but you already know this, because you have already attempted to implement the done callback.
Also, drop() will accept a callback function like so:
beforeEach((done) => {
mongoose.connection.collections.todo.drop(() => {});
});
The function will only be executed once you are done with the collection. So you also have to pass the done() callback after defining it like so:
beforeEach((done) => {
mongoose.connection.collections.todo.drop(() => {
done();
});
});
Also, when you run a Mocha test, you do npm run test.

I've just tried to replicate your issue with the test with simple repository using your package.json file. My test file
const expect = require('expect');
beforeEach((done) => {
done();
});
describe('just a test', function() {
it('test', function() {
expect(true).toBe(true);
})
});
Then, when running npm t, the test was executed successfully.
Perhaps there is a misconfiguration in your project.

Related

How can i speed up firebase functions in an express app which are taking too long to load taking more than 8 seconds most times

We have an application that uses a nodejs and express backend powered by firebase but one problem that we're facing is very long load times even for simple queries. We refractored our endpoints into different files so that they all don't run all at once and we also have cron jobs which i suspected would help with cold starts in which i'm convinced and doubt we experiencing as all the requests including subsquent req are just slow and they really take at most times more than 8 seconds which is a poor perfomance for users to experience.
in our package.json this what we have in order for you to see the firebase versions we using
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "14"
},
"dependencies": {
"#google-cloud/storage": "^5.8.2",
"#sendgrid/mail": "^7.2.1",
"algoliasearch": "^4.3.0",
"bcrypt": "^5.1.0",
"busboy": "^0.3.1",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"dayjs": "^1.10.4",
"dotenv": "^8.2.0",
"easy-soap-request": "^4.1.3",
"express": "^4.17.1",
"firebase": "^7.15.5",
"firebase-admin": "^8.6.0",
"firebase-functions": "^3.23.0",
"fs-extra": "^9.0.1",
"jwt-decode": "^2.2.0",
"moment": "^2.29.1",
"request": "^2.88.2",
"sharp": "^0.25.4",
"sib-api-v3-sdk": "^8.4.2",
"uuid": "^8.2.0",
"xml-js": "^1.6.11"
},
"devDependencies": {
"firebase-functions-test": "^0.1.6"
},
"private": true
}
below is the index.js file on how we set up everything.
require("dotenv").config();
const functions = require("firebase-functions");
const express = require("express");
const app = express();
const cookieParser = require("cookie-parser");
const cors = require("cors");
app.use(cors());
app.use(cookieParser());
app.use(express.json());
const dashboardRoutes = require("./routes/dashboardRoutes");
const userRoutes = require("./routes/userRoutes");
const pagesRoutes = require("./routes/pagesRoutes");
const orderRoutes = require("./routes/orderRoutes");
const cartRoutes = require("./routes/cartRoutes");
const wishlistRoutes = require("./routes/wishlistRoutes");
const authRoutes = require("./routes/authRoutes");
const storeRoutes = require("./routes/storeRoutes");
const createSellerRoutes = require("./routes/createSellerRoutes");
app.use("/", pagesRoutes);
app.use("/user", userRoutes);
app.use("/dash", dashboardRoutes);
app.use("/order", orderRoutes);
app.use("/cart", cartRoutes);
app.use("/wishlist", wishlistRoutes);
app.use("/auth", authRoutes);
app.use("/s", storeRoutes);
app.use("/cr", createSellerRoutes);
const {
cron_job1,
cron_job2,
cron_job3,
cron_job4,
} = require("./triggers/search_triggers_and_cron_jobs"); <-- not the name of the actual file
const { **other cron jobs** } = require("./cron-jobs");
const {
update_exchange_currency_rates,
} = require("./cron-jobs/currency_exchange_rates");
const { reset_product_visits } = require("./triggers/products");
const { Home } = require("./handlers/pages");
const { db } = require("./util/admin");
const { product_basic_obj } = require("./util/product_basic_obj");
exports.apis = functions.https.onRequest(app);
// this functionality below is the sample for the kind of executions we perfom in the endpoints in which we also experience slow execution times ven for a function in this file
app.get("/test-home", (req, res) => {
let content = {};
db.collection("products")
.where("status", "==", "active")
.orderBy("visited", "desc")
.limit(20)
.get()
.then((data) => {
content.popular_today = [];
data.forEach((x) => {
content.popular_today.push(product_basic_obj(x.data()));
});
return db
.collection("products")
.where("status", "==", "active")
.orderBy("todaysSales", "desc")
.limit(20)
.get();
})
.then((data) => {
content.hot_today = [];
data.forEach((x) => {
content.hot_today.push(product_basic_obj(x.data()));
});
return db.collection("departments").get();
})
.then((data) => {
content.departments = [];
data.forEach((x) => {
content.departments.push(x.data());
});
return db
.collection("departments")
.orderBy("products_sold_today", "desc")
.limit(6)
.get();
})
.then((data) => {
content.top_departments = [];
data.forEach((x) => {
content.top_departments.push(x.data());
});
return res.status(200).json(content);
});
});
//cron jobs
exports.cron_job1 = cron_job1;
exports.cron_job2 = cron_job2;
exports.cron_job3 = cron_job3;
exports.cron_job4 = cron_job4;
Upon executing an end point this is what shows in the consol and the in a deployed development we experience the same slow execution times which seems to be the average for all our executions
i functions: Beginning execution of "us-central1-apis"
⚠ Google API requested!
- URL: "https://www.googleapis.com/oauth2/v4/token"
- Be careful, this may be a production service.
i functions: Finished "us-central1-apis" in ~9s
i functions: Finished "us-central1-apis" in ~8s
i functions: Beginning execution of "us-central1-apis"
i functions: Finished "us-central1-apis" in ~7s
i functions: Beginning execution of "us-central1-apis"
i functions: Finished "us-central1-apis" in ~7s
i functions: Beginning execution of "us-central1-apis"
i functions: Finished "us-central1-apis" in ~7s
i functions: Beginning execution of "us-central1-apis"
i functions: Finished "us-central1-apis" in ~6s
i functions: Beginning execution of "us-central1-apis"
i functions: Finished "us-central1-apis" in ~7s
i functions: Beginning execution of "us-central1-apis"
i functions: Finished "us-central1-apis" in ~7s
How can i speed up the execution using the information above.
We tried breaking code into smaller files which couldn't work as we expected to get more faster excecutions and we also removed most of our 3rd part libraries but we failed to make a change. What could we do to bring down executions times futher down.
Your data loading strategy is sequential, Load 1 then Load2 then Load3, and in case none of the following Load depends on the result of previous Load - that aproach is not really effective and useful.
Instead - you can try to utilize Promise.all() to fire all that promises "in parallel".
Next issue - you are loading departments from firebase twice, actual departments and top_departments, and there is no need to load top_departments again due to all the data that is needed is already in departments, you only need to .sort and slice them (or its shallow copy [...departments]).
I'd try this approach:
// popular_today
function getPopularProductsByVisitedAsync() {
return db
.collection("products")
.where("status", "==", "active")
.orderBy("visited", "desc")
.limit(20)
.get()
.then((data) => {
return data.docs.map((x) => product_basic_obj(x.data()));
});
}
// hot_today
function getPopularProductsByTodaySalesAsync() {
return db
.collection("products")
.where("status", "==", "active")
.orderBy("todaysSales", "desc")
.limit(20)
.get()
.then((data) => {
return data.docs.map((x) => product_basic_obj(x.data()));
});
}
function getAllDepartmentsAsync() {
return db
.collection("departments")
.get()
.then((data) => data.docs.map((x) => x.data()));
}
app.get("/test-home", async (req, res) => {
const [popular_today, hot_today, departments] = await Promise.all([
getPopularProductsByVisitedAsync(),
getPopularProductsByTodaySalesAsync(),
getAllDepartmentsAsync()
]);
// TODO: Check asc/desc, im not sure, was not testing
const top_departments = [...departments]
.sort((a, b) => a.products_sold_today - b.products_sold_today)
.slice(0, 6);
const content = {
popular_today: popular_today,
hot_today: hot_today,
departments: departments,
top_departments: top_departments
};
return res.status(200).json(content);
});
Try to execute your requests in parallel on the index.js.
This optimization will provide some gains on the network request timings.

Firebase functions crashes while accessing firestore

Could you please find an error in following code?
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.GetShort = functions.https.onRequest((request, response) => {
response.header("Access-Control-Allow-Origin", "*");
longURL = request.query.long
functions.logger.info("url is - " ,longURL)
SaveToDB(longURL)
})
function SaveToDB(link){
functions.logger.info("here")
admin.firestore().collection("url").where("urlNames","array_contains",link).get().then(
function(querySnapshot){
functions.logger.info("snap, " ,querySnapshot)
querySnapshot.forEach(function(doc) {
functions.logger.info("things : " ,doc.id, " => ", doc.data())
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
}
) .catch(function(error) {
functions.logger.info("Error getting documents: ", error);
});
}
After hitting above function, firebase-functions logs displays logs till "here". After that it crashes without any more logs/stacktrace.
below is the contents of packages.json from functions directory.
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "14"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^9.8.0",
"firebase-functions": "^3.14.1"
},
"devDependencies": {
"eslint": "^7.6.0",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^0.2.0"
},
"private": true
}
I would kindly suggest you watch the 3 videos about "JavaScript Promises" from the Firebase video series to see how to manage the life cycle of a Cloud Function and the way to deal with calls to asynchronous methods.
In particular, for an HTTPS Cloud Function, you need to end it with send(), redirect(), or end().
So your code could be adapted as follows:
exports.GetShort = functions.https.onRequest((request, response) => {
response.header("Access-Control-Allow-Origin", "*");
const longURL = request.query.long;
functions.logger.info("url is - ", longURL)
SaveToDB(longURL)
.then(() => {
response.status(200).send('Saved to DB');
})
.catch(error => {
// See video series
response.status(500).send(error);
})
})
function SaveToDB(link) {
functions.logger.info("here")
return admin.firestore().collection("url").where("urlNames", "array_contains", link).get()
.then(querySnapshot => {
functions.logger.info("snap, ", querySnapshot)
querySnapshot.forEach(function (doc) {
functions.logger.info("things : ", doc.id, " => ", doc.data())
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
// => Here, depending on your real functional requirements, you may need to use Promise.all()
});
return null;
}
).catch(function (error) {
functions.logger.info("Error getting documents: ", error);
// Throw an error
});
}
Well, I found the problem. Sometimes silly things make most of the noise around.
Instead of "array-contains", I wrote "array_contains".

Unit testing of HttpsCallable cloud functions - online mode

I am designing a backend api (for android and iOS apps) around HttpsCallable cloud functions. It becomes quite cumbersome to test them through the app, so I wish to switch to unit testing the functions (before production deployment) using the firebase-functions-test tool. I have been following this unit testing tutorial.
I am having some issues running the unit tests in online mode. Let me provide some details.
Here is my `package.json' content:
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"test": "mocha --reporter spec"
},
"engines": {
"node": "8"
},
"dependencies": {
"#google-cloud/tasks": "^1.9.0",
"#googlemaps/google-maps-services-js": "^2.0.2",
"chai": "^4.2.0",
"firebase-admin": "^8.6.0",
"firebase-functions": "^3.3.0",
},
"devDependencies": {
"eslint": "^5.12.0",
"eslint-plugin-promise": "^4.0.1",
"firebase-functions-test": "^0.1.7",
"mocha": "^7.1.1"
},
"private": true
}
I am using google APIs (Directions, Geocoding etc) from firebase backend, therefore to be able to access them while running my tests, I configured my tests located at test/index.test.js as recommended in the Unit testing tutorial as follows:
const firebaseConfig = {
apiKey: ...,
authDomain: ...,
databaseURL: "https://my-project.firebaseio.com",
projectId: "my-project",
storageBucket: "my-project.appspot.com",
messagingSenderId: ********,
appId: *********
};
const test = require('firebase-functions-test')(firebaseConfig
, 'path_to_json_keyfile/myproject.json');
Below is the sample test code. Note that all my callable functions return HttpsError, and the test below, simply checks for the code field of HttpsError, if its ok, the test passes. The functions I am testing reside in a separate rides.js file, which the index.js exports as exports.rides = require(./rides.js) (not shown below)
const chai = require('chai');
const assert = chai.assert;
describe('Cloud functions', () => {
let rideFunctions;
before(() => {
rideFunctions = require('../index.js');
});
after(() => {
test.cleanup();
});
describe('getRideEstimate', () => {
it('should return ok', async () => {
data = {
riderType: 1,
pickup_lat: 37.0,
pickup_lng: -122,
dropoff_lat: 37.01,
dropoff_lng: -122.01,
scheduledTime: 1585939000,
ts: 1585939000,
tz_id: "uslax"
}
context = {
auth: {
uid: AUTH_ID_FOR_USER_ACCOUNT
}
};
const wrappedGetRideEstimate = test.wrap(rideFunctions.rides.getRideEstimate);
let httpsError = await wrappedGetRideEstimate(data, context);
return assert.equal(httpsError.code, 'ok');
});
})
describe('testCallable', () => {
it('should return ok', async () => {
data = {}
context = {}
const wrappedTestCallable = test.wrap(rideFunctions.rides.testCallable);
let httpsError = await wrappedTestCallable(data, context);
return assert.equal(httpsError.code, 'ok');
})
})
})
Problem
My simple testCallable function of the form
exports.testCallable = functions.https.onCall((data, context) => {
console.log('testCallable');
return new functions.https.HttpsError('ok', '', '');
})
passes the test (as expected) but inexplicably, it seems it is running in the offline mode, as there is no record of it on cloud function logs in Firebase Console. Also, if I disable connectivity of my laptop, the test result remains the same suggesting that somehow this function is running in the offline mode.
My getRideEstimate function which calls the google Directions API, returns a lengthy 400 error indicating some issue with forming the request to Directions API. I don't know if this error is related to the first problem, but since the Directions API call is embedded deeper inside the getRideEstimate function, it does suggest that the function is getting called, but somehow the API call is failing.
Is there any other way to test HttpsCallable functions in online mode?
For me this works:
import * as firebaseSetup from 'firebase-functions-test';
export const testEnvironment = firebaseSetup({
databaseURL: "https://project-id.firebaseio.com",
projectId: 'project-id'
}, './service-account-project-id-firebase-adminsdk.json');
You can find a full YouTube tutorial under this: https://youtu.be/UDMDpdu5-rE?t=1122

React-Native app doesn't work if i use await

This is the first time I'm using react-native to develop an Android app.
I'm trying to use async/await when I retrieve some data from firestore, but when I add the word await the app crashes and it won't start anymore.
I use react-native-firebase library as you can see in my package.json file.
This is the part of my component that doesn't work:
componentDidMount() {
this.getAccountFromFirestore(this.props.user);
}
getAccountFromFirestore = async user => {
const accounts = await firebase
.firestore()
.collection('accounts')
.get();
this.setState({ loading: false });
};
This is my package.json, if it's usefull
{
"name": "myapp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest",
"ios": "react-native run-ios",
"android": "react-native run-android"
},
"dependencies": {
"native-base": "^2.8.1",
"react": "16.6.1",
"react-native": "0.57.6",
"react-native-firebase": "^5.1.1",
"react-native-navigation": "^1.1.493",
"react-native-vector-icons": "^6.1.0",
"react-redux": "^5.1.1",
"redux": "^4.0.1"
},
"devDependencies": {
"babel-jest": "23.6.0",
"jest": "23.6.0",
"metro-react-native-babel-preset": "0.49.2",
"react-test-renderer": "16.6.1"
},
"jest": {
"preset": "react-native"
}
}
You can implement with Promise like :
firebase
.firestore()
.collection('accounts')
.get()
.then(accounts => {
console.log ('Use accounts variable as per your requirement,'accounts)
})
.catch(error => {console.error(error)})
I don't know whether it's your React Native or if you're using Node 6. But for what I know, async/await is only supported on Node 8 or above. So, your best bet is still using Promises with this problem. For example:
componentDidMount() {
this.getAccountFromFirestore(this.props.user);
}
getAccountFromFirestore = user => {
firebase
.firestore()
.collection('accounts')
.get()
.then(accounts => {
let data = accounts.docs.map(account => account.data());
console.log(data);
this.setState({ loading: false });
})
.catch(err => console.log(err));
};
Also when using async/await, don't forget the try-catch blocks:
componentDidMount() {
this.getAccountFromFirestore(this.props.user);
}
getAccountFromFirestore = async user => {
try {
const accounts = await firebase
.firestore()
.collection('accounts')
.get();
const data = accounts.docs.map(a => a.data());
console.log(data);
this.setState({ loading: false });
} catch (err) {
console.error(err);
}
};
Option a) keep using promises
Option b) Migrate that RN, if you can use async/await your code is prob too old to go in the stores anyway.
Option c) Babel...

Why is my Mocha/Chai test giving a false positive?

As a pre-cursor to this question, I am a novice at Node, JS, Mocha and Chai!
I have a set of tests which I run using npm run start, in which 'start' defines a script within my Package.json file:
"devDependencies": {
"chai": "^3.5.0",
"chai-as-promised": "^7.1.1",
"cli-color": "^1.1.0",
"concurrently": "^3.1.0",
"mocha": "^5.2.0"
},
"dependencies": {
"body-parser": "^1.16.1",
"cors": "^2.8.1",
"express": "^4.14.0",
"moment": "^2.18.1",
"superagent": "^3.3.2"
}
Here is my test:
const expect = require('chai').expect;
const screenshotFolder = 'puppeteer/test/screenshots';
module.exports = async(page) => {
const frame = page.frames().find(frame => frame.name() === 'iframe');
const allChoicesButton = await frame.$('.statement a.all-choices');
await allChoicesButton.click({});
const saveYourChoicesButton = await frame.$('.button.permissions-block__submit');
await saveYourChoicesButton.click({});
try {
const confirmationMessageText = await frame.$eval('.submission-response__copy > p', e => e.textContent);
describe('User can choose all', function() {
it('Click choose all and display a confirmation message', function(done) {
expect(confirmationMessageText).to.equal('Thank you. Your choices have been updatedx.').
notify(done)
});
});
} catch (err) {
await page.screenshot({
path: screenshotFolder + '/confirmationMessageText.png',
fullPage: true
});
}
};
I have purposefully added an 'x' to 'updatedx' so it fails....only it passes. So, I'm sure this has been asked 100 times but I'm unclear as to why it is passing and also why the screenshot is printed, given that an error is not thrown.
Thanks in advance.
I've realised the error now, swapped frame.$eval with frame.$ and this has resolved the issue.

Categories

Resources