Accessing Meteor settings in Mocha - node.js

I want to do write some unit tests with mocha but for some tests i need to access the Meteor.settings which are read by meteor from a file: config/settings.json
Normally i import meteor by:
import { Meteor } from 'meteor/meteor'
But when i try to import this in my test, i get the error: ERROR: Cannot find module 'meteor/meteor' (i also tried to do relative path).
I run my test by this:
"wdio-test": "wdio tests/config/wdio.mocha.conf.js"
and
npm run wdio-test
Anyone can help importing Meteor or accessing the settings file (If possible without file IO operations)?

Stub the meteor core and the settings object:
import { MeteorStubs } from 'meteor/velocity:meteor-stubs';
describe('tests', function() {
beforeEach(function() {
MeteorStubs.install();
Meteor.settings.public.foo = 'bar';
});
afterEach(function() {
MeteorStubs.uninstall();
});
it('gets setting', function() {
chai.assert.equal(Meteor.settings.public.foo, 'bar');
});
});

Related

How to mock electron when running jest with #kayahr/jest-electron-runner

Setup
I want to unit test my electron app with jest. For this I have the following setup:
I use jest and use #kayahr/jest-electron-runner to run it with electron instead of node. Additionally, since it is a typescript project, I use ts-jest.
My jest.config.js looks like this:
module.exports = {
collectCoverage: true,
coverageDirectory: 'coverage',
coverageProvider: 'v8',
preset: 'ts-jest',
runner: '#kayahr/jest-electron-runner/main',
testEnvironment: 'node',
};
The test is expected to run in the main process. I have reduced my code to the following example function:
import { app } from 'electron';
export function bar() {
console.log('in bar', app); //this is undefined when mocked, but I have a real module if not mocked
const baz = app.getAppPath();
return baz;
}
The test file:
import electron1 from 'electron';
import { bar } from '../src/main/foo';
console.log('in test', electron1); //this is undefined in the test file after import
// jest.mock('electron1'); -> this does just nothing, still undefined
const electron = require('electron');
console.log('in test after require', electron); //I have something here yay
jest.mock('electron'); //-> but if I comment this in -> it is {} but no mock at all
it('should mock app', () => {
bar();
expect(electron.app).toBeCalled();
});
What do I want to do?
I want to mock electron.app with jest to look whether it was called or not.
What is the problem?
Mocking electron does not work. In contrast to other modules like fs-extra where jest.mock() behaves as expected.
I don't understand the behavior happening here:
Importing "electron" via import in the file containing the tests (not the file to be tested!) does not work (other modules work well), but require("electron") does.
I do have the electron module if not mocked in bar(), but after mocking not
while jest.mock("fs-extra") works, after jest.mock("electron") electron is only an empty object, not a mock
I would really like to understand what I did wrong or what the problem is. Switching back to #jest-runner/electron does not seem to be an option, since it is not maintained anymore. Also I don't know if this is even the root of the problem.
Has anyone seen this behavior before and can give me a hint where to start searching?

Jest test passed but get Error: connect ECONNREFUSED 127.0.0.1:80 at the end

I'm using node with TypeScript on my back end and Jest and Supertest as my test framework on my back end.
When I'm trying to test I have the result pass but I get an error at the end. Here's the result:
PASS test/controllers/user.controller.test.ts
Get all users
✓ should return status code 200 (25ms)
console.log node_modules/#overnightjs/logger/lib/Logger.js:173
[2019-12-05T04:54:26.811Z]: Setting up database ...
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.284s
Ran all test suites.
server/test/controllers/user.controller.test.ts:32
throw err;
^
Error: connect ECONNREFUSED 127.0.0.1:80
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1104:14)
npm ERR! Test failed. See above for more details.
Here's my test code:
import request from "supertest";
import { AppServer } from '../../config/server';
const server = new AppServer();
describe('Get all users', () => {
it('should return status code 200', async () => {
server.startDB();
const appInstance = server.appInstance;
const req = request(appInstance);
req.get('api/v1/users/')
.expect(200)
.end((err, res) => {
if (err) throw err;
})
})
})
Here's my server setup. I'm using overnightjs on my back end.
I created a getter to get the Express instance. This is coming from overnight.js.
// this should be the very top, should be called before the controllers
require('dotenv').config();
import 'reflect-metadata';
import { Server } from '#overnightjs/core';
import { Logger } from '#overnightjs/logger';
import { createConnection } from 'typeorm';
import helmet from 'helmet';
import * as bodyParser from 'body-parser';
import * as controllers from '../src/controllers/controller_imports';
export class AppServer extends Server {
constructor() {
super(process.env.NODE_ENV === 'development');
this.app.use(helmet());
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: true }));
this.setupControllers();
}
get appInstance(): any {
return this.app;
}
private setupControllers(): void {
const controllerInstances = [];
// eslint-disable-next-line
for (const name of Object.keys(controllers)) {
const Controller = (controllers as any)[name];
if (typeof Controller === 'function') {
controllerInstances.push(new Controller());
}
}
/* You can add option router as second argument */
super.addControllers(controllerInstances);
}
private startServer(portNum?: number): void {
const port = portNum || 8000;
this.app.listen(port, () => {
Logger.Info(`Server Running on port: ${port}`);
});
}
/**
* start Database first then the server
*/
public async startDB(): Promise<any> {
Logger.Info('Setting up database ...');
try {
await createConnection();
this.startServer();
Logger.Info('Database connected');
} catch (error) {
Logger.Warn(error);
return Promise.reject('Server Failed, Restart again...');
}
}
}
I read this question - that's why I called the method startDB.
So I figured out and the solution is quite easy. I can't explain why though.
This req.get('api/v1/users/') should be /api/v1/users - you need a leading /.
For Frontend...
If you are making use of axios and come across this error, go to the testSetup.js file and add this line
axios.defaults.baseURL = "https://yourbaseurl.com/"
This worked for me. So, typically, this is a baseURL issue.
I had this error in my React frontend app tests.
I was using React testing library's findBy* function in my assert:
expect(await screen.findByText('first')).toBeInTheDocument();
expect(await screen.findByText('second')).toBeInTheDocument();
expect(await screen.findByText('third')).toBeInTheDocument();
After I changed it to:
await waitFor(async () => {
expect(await screen.findByText('first')).toBeInTheDocument();
expect(await screen.findByText('second')).toBeInTheDocument();
expect(await screen.findByText('third')).toBeInTheDocument();
});
the error is gone.
I don't know exactly why, but maybe it will help someone
UPDATE: I was mocking fetch incorrectly, so my test called real API and caused that error
I put this line in my setupTests file:
global.fetch = jest.fn()
It mocks fetch for all tests globally. Then, you can mock specific responses right in your tests:
jest.mocked(global.fetch).mockResolvedValue(...)
// OR
jest.spyOn(global, 'fetch').mockResolvedValue(...)
Slightly different issue, but same error message...
I was having this error when using node-fetch when trying to connect to my own localhost (http://localhost:4000/graphql), and after trying what felt like everything under the sun, my most reliable solution was:
using this script in package.json: "test": "NODE_ENV=test jest --watch"
If the terminal shows connection error I just go to the terminal with Jest watching and press a to rerun all tests and they pass without any issue.
¯\_(ツ)_/¯
Success rate continued to improve by renaming the testing folder to __tests__ and moving my index.js to src/index.js.
Very strange, but I am too exhausted to look at the Jest internals to figure out why.
The rules for supertest are the same as the rules for express. OvernightJS does not require any leading or ending "/" though.
For anyone landing on this, but not having issues with trailing slashes:
jest can also return a ECONNREFUSED when your express app takes some time (even just a second) to restart/init. If you are using nodemon like me, you can disable restarts for test files like --ignore *.test.ts.
This error also occurs if you have not set up a server to catch the request at all (depending on your implementation code and your test, the test may still pass).
I didn't get to the bottom of this error - it wasn't related to the (accepted) leading slash answer.
However, my "fix" was to move the mocks up into the suite definition - into beforeAll and afterAll for cleanup between tests).
Before, I was mocking (global.fetch) in each test, and it was the last test in the suite to use the mock that would cause the error.
In my case, the issue was related to package react-inlinesvg. Package makes a fetch request to get the svg file and since server is not running, it gets redirected to default 127.0.0.1:80.
I mocked react-inlinesvg globally to output props including svg filename to assert in testing.
jest.mock('react-inlinesvg', () => (props) => (
<svg data-testid="mocked-svg">{JSON.stringify(props)}</svg>
));

Gulp + Browserify + TypeScript To Browser

My problem is the following:
I use gulp+browserify to compile my TypeScript to JavaScript that you can use on normal HTML pages, the problem is that my class is never available on the browser:
VM633:1 Uncaught ReferenceError: Test is not defined
at <anonymous>:1:13
This is my TypeScript File:
class Test {
public test(): void {
console.log("aa");
}
}
This is my gulpfile
var gulp = require("gulp");
var browserify = require("browserify");
var source = require('vinyl-source-stream');
var tsify = require("tsify");
gulp.task("default", function () {
return browserify({
//basedir: '.',
debug: true,
entries: ['app/Resources/typescript/Test.ts'],
cache: {},
packageCache: {}
})
.plugin(tsify)
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest("web/bundles/framework/js"));
});
The file compiles without problem, and is included in my index.html (the compiled js file).
But when i try:
var t = new Test();
I get the following error:
VM633:1 Uncaught ReferenceError: Test is not defined
at <anonymous>:1:13
I can't resolve it, I have read a lot and I haven't found anything clear, I tried all and nothing worked.
There are a few things missing here:
If you want your class to be accessible outside of your module, you have to export it:
export class Test {
// ...
}
Browserify creates functions on top of the classes you define. So it won't be accessible globally (which is a good thing). Normally you would import it in another file and use it:
// in any TS file that wants to use `Test` class. Make sure this is included in the gulp entries as well
import {Test} from "test";
var t = new Test();
console.log(t);
Or if really want it to be accessible globally, you can attach it to window object:
// In Test.ts file:
(window as any).Test = Test; // This can be useful when debuging. But don't do this in production code.

Is there a jest config that will fail tests on console.warn?

How do I configure jest tests to fail on warnings?
console.warn('stuff');
// fail test
You can use this simple override :
let error = console.error
console.error = function (message) {
error.apply(console, arguments) // keep default behaviour
throw (message instanceof Error ? message : new Error(message))
}
You can make it available across all tests using Jest setupFiles.
In package.json :
"jest": {
"setupFiles": [
"./tests/jest.overrides.js"
]
}
Then put the snippet into jest.overrides.js
For those using create-react-app, not wanting to run npm run eject, you can add the following code to ./src/setupTests.js:
global.console.warn = (message) => {
throw message
}
global.console.error = (message) => {
throw message
}
Now, jest will fail when messages are passed to console.warn or console.error.
create-react-app Docs - Initializing Test Environment
I implemented this recently using jest.spyOn introduced in v19.0.0 to mock the warn method of console (which is accesses via the global context / object).
Can then expect that the mocked warn was not called, as shown below.
describe('A function that does something', () => {
it('Should not trigger a warning', () => {
var warn = jest.spyOn(global.console, 'warn');
// Do something that may trigger warning via `console.warn`
doSomething();
// ... i.e.
console.warn('stuff');
// Check that warn was not called (fail on warning)
expect(warn).not.toHaveBeenCalled();
// Cleanup
warn.mockReset();
warn.mockRestore();
});
});
There is a useful npm package that helps you to achieve that: jest-fail-on-console
It's easily configurable.
Install:
npm i -D jest-fail-on-console
Configure:
In a file used in the setupFilesAfterEnv option of Jest, add this code:
import failOnConsole from 'jest-fail-on-console'
failOnConsole()
// or with options:
failOnConsole({ shouldFailOnWarn: false })
I decided to post a full example based on user1823021 answer
describe('#perform', () => {
var api
// the global.fetch is set to jest.fn() object globally
global.fetch = jest.fn()
var warn = jest.spyOn(global.console, 'warn');
beforeEach(function() {
// before every test, all mocks need to be resetted
api = new Api()
global.fetch.mockReset()
warn.mockReset()
});
it('triggers an console.warn if fetch fails', function() {
// In this test fetch mock throws an error
global.fetch.mockImplementationOnce(() => {
throw 'error triggered'
})
// I run the test
api.perform()
// I verify that the warn spy has been triggered
expect(warn).toHaveBeenCalledTimes(1);
expect(warn).toBeCalledWith("api call failed with error: ", "error triggered")
});
it('calls fetch function', function() {
// I create 2 more mock objects to verify the fetch parameters
const url = jest.fn()
const config = jest.fn()
api.url = url
api.config = config
// I run the test
api.perform()
// I verify that fetch has been called with url and config mocks
expect(global.fetch).toHaveBeenCalledTimes(1)
expect(global.fetch).toBeCalledWith(url, config)
expect(warn).toHaveBeenCalledTimes(0)
});
})
the #perform method I am testing
class Api {
constructor(auth) {
this._credentials = auth
}
perform = async () => {
try {
return await fetch(this.url, this.config)
} catch(error) {
console.warn('api call failed with error: ', error)
}
}
}
You can set the environment variable CI=true before running jest which will cause it to fail tests on warnings in addition to errors.
Example which runs all test files in the test folder:
CI=true jest ./test
Automated CI/CD pipelines such as Github Actions set CI to true by default, which can be one reason why a unit test will pass on your local machine when warnings are thrown, but fail in the pipeline.
(Here is the Github Actions documentation on default environment variables: https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables)

How can we use jasmine, jasmine.ConsoleReporter, require.js and backbone together

I'm struggling to get jasmine along with it's ConsoleReporter working within a backbone application using require.js. I have seen Check Backbone/requireJs project with Jasmine but that hardcodes the libraries (which is something that I'd prefer to avoid).
In my backbone application I have created test function (I'd prefer to keep it there to test interactions between models):
test = function () {
require(['js/test/run'], function () {});
}
and run.js (I get the console.log "should" fine, but don't get anything to do with the failed test):
define(["jasmine", "jasmineConsoleReporter"],
function (jasmine, ConsoleReporter) {
describe('hello', function () {
it('should be true', function () {
console.log('should');
expect(true).toEqual(true);
});
});
jasmine.getEnv().addReporter(new ConsoleReporter(console.log));
jasmine.getEnv().execute();
//return tests;
}
);
The shim for jasmine and jasmineConsoleReporter are:
jasmine: {
exports: "jasmine"
},
jasmineConsoleReporter: {
deps: ['jasmine'],
exports: "getJasmineRequireObj"
}
And the source for jasmineConsoleReporter can be found at https://github.com/pivotal/jasmine/blob/master/src/console/console.js
I'm guessing that the console reporter isn't being constructed correctly because I get the 'should' in the console and nothing else.
Try my setup:
https://github.com/wojciechszela/jasmine-requirejs-jscover
Adding backbone to it (or any other lib) should be easy.

Resources