Is it possible to change dynalite settings when using jest-dynalite - jestjs

My jest tests are very slow at the moment and I suspect that one of the reasons is that dynality has slow defaults, such as an intentional delay on all updates. Is it possible to change these settings in a jest-dynalite environment?
My jest.config.js:
module.exports = {
"preset": 'ts-jest/presets/default-esm',
...
"testEnvironment": "jest-dynalite/environment.js"
};
I tried exploring the import * as dynalite from 'jest-dynalite'; object but did not get far.

It appears that this is already done, can be seen here:
export const dynaliteInstance = dynalite({
createTableMs: 0,
deleteTableMs: 0,
updateTableMs: 0,
});

Related

"Object.defineProperty(exports, "__esModule", { value: true });" blocks functions execution in FunctionFile

I started a Office Web Add-in with Typescript&React project by following this tutorial: https://github.com/OfficeDev/office-js-docs-pr/blob/master/docs/includes/file-get-started-excel-react.md . Any taskpane function and page works properly, but functions on the function-file page cannot be properly executed.
By deleting code, I found Object.defineProperty(exports, "__esModule", { value: true }); is one of line in compiled function-file.js casing the problem. Whenever it presents, any function in the file won't be executed. Fiddler shows the script is correctly loaded in Excel without any warning. Status bar shows "[add-in name] is working on your [function name]".
This line of code is generated by Typescript Compiler, in this case, for loading Node module '#microsoft/office-js-helpers'. I tried to modify tsconfig.json file to avoid generating that line, but then the import of '#microsoft/office-js-helpers' fails. In addition, Webpack 4 will add webpackBootstrap code blocking functions in this file. At this point, I can only avoid any import in function-file.ts and do a 'tsc' after building the project by Webpack.
My question is: what is the correct way to setup this project so function-file.js does not contain any code blocking its functions being executed?
If there is no clear answer, at least, why this line of code causes problem where other pages work fine?
The following is my tsconfig.json which can avoid that line but cannot load any module:
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"lib": ["es2015", "dom"],
"typeRoots": ["node_modules/#types"]
},
I manually edit the compiled function-file.js into two versions:
Object.defineProperty(exports, "__esModule", { value: true });
(function () {
Office.initialize = function () { }
};
})();
function writeText(event) {
Office.context.document.setSelectedDataAsync('test');
event.completed();
}
VS
(function () {
Office.initialize = function () { }
};
})();
function writeText(event) {
Office.context.document.setSelectedDataAsync('test');
event.completed();
}
The first one has this problem whereas the second one doesn't.
With some hints from my colleague who used to work on JavaScript during a lunch talk, I made some progress of calling functions in function-file.ts. I wish my path of getting this work would help other people suffering the same pain as I did and still do on this project.
First of all, once I got the function-file.js works properly, I noticed there are two different behaviours when a function does not work:
status bar shows "[add-in name] is working on your [function name]" and stays with it, I believe the function is called but not the line of event.completed() couldn't be reached;
status bar flashes the same message and becomes blank, which indicates the function is not even been found.
Please correct me if there is a better way to diagnose this file.
The original Yeoman generated Webpack configuration of function-file.html is something like this:
new HtmlWebpackPlugin({
title: 'demo',
filename: 'function-file/function-file.html',
template: '../function-file/function-file.html',
chunks: ['function-file']
}),
In order to use any module, 'vendor'(not necessary for my custom modules, but needed by 'office-js-helpers'?) and 'polyfills' entry needs to be included in chunks as well.
Mine Webpack 4 configuration is:
new HtmlWebpackPlugin({
title: "demo",
filename: "function-file/function-file.html",
template: "../function-file/function-file.html",
chunks: ["babel-polyfill", "function-file/function-file"]
}),
The last step is making sure functions declared in function-file.ts can be found: asking Webpack to export global functions in function-file.ts, which I am still not sure if I am hacking Typescript development or doing fine.
Sample function-file.ts:
import * as OfficeHelpers from '#microsoft/office-js-helpers';
(() => {
Office.initialize = () => {};
})();
declare global {
namespace NodeJS {
interface Global {
writeText: (event: Office.AddinCommands.Event) => void;
}
}
}
global.writeText = (event: Office.AddinCommands.Event) => {
Office.context.document.setSelectedDataAsync('test');
event.completed();
};
Notice: even office-js-helpers is imported, some of functions are still not working. I tested my custom modules, they are working properly.
I really wish there are some function-file examples on NodeJS hosted React&Typescript project for Office Web Add-in, as detail configuration is really different from ordinary NodeJS + JavaScript project.

"Write after end": how to imitate gulp-watch with watchify?

The following Gulp task does almost what I want.
const gulp = require('gulp');
const browserify = require('browserify');
const vinylStream = require('vinyl-source-stream');
const vinylBuffer = require('vinyl-buffer');
const watchify = require('watchify');
const glob = require('glob');
const jasmineBrowser = require('gulp-jasmine-browser');
gulp.task('test', function() {
let testBundler = browserify({
entries: glob.sync('src/**/*-test.js'),
cache: {},
packageCache: {},
}).plugin(watchify);
function updateSpecs() {
return testBundler.bundle()
.pipe(vinylStream(jsBundleName))
.pipe(vinylBuffer())
.pipe(jasmineBrowser.specRunner({console: true}))
.pipe(jasmineBrowser.headless({driver: 'phantomjs'}));
}
testBundler.on('update', updateSpecs);
updateSpecs();
});
It bundles all my Jasmine specs using Browserify and has them tested through gulp-jasmine-browser. It also watches all specs and all modules that they depend on and re-runs the tests if any of these modules changes.
The only ugly bit, which I'd really like to see solved, is that a new PhantomJS instance and a new Jasmine server are created every time updateSpecs is run. I was hoping to avoid that with code like the following:
gulp.task('test', function() {
let testBundler = browserify({
entries: glob.sync('src/**/*-test.js'),
cache: {},
packageCache: {},
}).plugin(watchify);
// persist the Jasmine server and PhantomJS browser
let testServer = jasmineBrowser.headless({driver: 'phantomjs'});
function updateSpecs() {
return testBundler.bundle()
.pipe(vinylStream(jsBundleName))
.pipe(vinylBuffer())
.pipe(jasmineBrowser.specRunner({console: true}))
.pipe(testServer);
}
testBundler.on('update', updateSpecs);
updateSpecs();
});
Alas, this doesn't work. Right after starting the task, all tests run fine, but the next time updateSpecs is called, I get a write after end error and the task exits with status 1. This error originates from the readable-stream Node module.
As I understand it, the end event during the first run of updateSpecs leaves testServer in a state in which it doesn't accept any new inputs. Unfortunately, the Node.js streams documentation isn't very clear on how to remedy this.
I have tried breaking the pipe chain at a different place, but I got the same result, which seems to indicate this is universal behaviour for streams. I also tried stopping the end event from propagating by inserting a through-stream that didn't re-emit that event, but this prevented the tests from being run at all. Finally, I tried returning the testServer stream from the task; this stopped the error, but although the updateSpecs function gets called every time the sources change, the tests are only being run the first time the task starts. This time, the testServer simply seems to ignore the new input.
The gulp-jasmine-browser documentation suggests that the following code would work:
var watch = require('gulp-watch');
gulp.task('test', function() {
var filesForTest = ['src/**/*.js', 'spec/**/*-test.js'];
return gulp.src(filesForTest)
.pipe(watch(filesForTest))
.pipe(jasmineBrowser.specRunner())
.pipe(jasmineBrowser.server());
});
And it goes on to suggest that you can also make this work with Browserify, but this isn't illustrated. Apparently, gulp-watch does something which causes the follow-up pipes to accept updated inputs later. How can I imitate this behaviour with watchify?
GitHub issue
As it turns out, it is a hard rule in Node.js that you cannot write after the end event. In addition, jasmineBrowser.specRunner(), .server() and .headless() must receive the end signal in order to actually test anything. This restriction is inherited from the official Jasmine test runner.
The example with gulp-watch from the README doesn't actually work, either, for the same reason. In order to make it work, one would have to do something similar to the working version of my watchify code in the question:
gulp.task('test', function() {
var filesForTest = ['src/**/*.js', 'spec/**/*-test.js'];
function runTests() {
return gulp.src(filesForTest)
.pipe(jasmineBrowser.specRunner())
.pipe(jasmineBrowser.server());
}
watch(filesForTest).on('add change unlink', runTests);
});
(I didn't test it, but something very close to this should work.)
So whatever watching mechanism you're using, you'll always need to call .specRunner() and .server() again for every cycle. The good news is that apparently, the Jasmine server will be reused if you explicitly pass a port number:
.pipe(jasmineBrowser.server({port: 8080}));
this also applies to .headless().

express body-parser utf-8 error in test

Super stumped by this. I have some server code that for some reason throws a UTF-8 error in my tests but works fine when running the server normally:
code:
export default ({ projectId = PROJECT_ID, esHost = ES_HOST } = {}) => {
let app = express();
app.use(cors());
app.use(bodyParser.json({ limit: '50mb' }));
let http = Server(app);
let io = socketIO(http);
let server = {
app,
io,
http,
status: 'off',
listen(
port = PORT,
cb = () => {
rainbow(`⚡️ Listening on port ${port} ⚡️`);
},
) {
this.http.listen(port, () => {
main({ io, app, projectId, esHost, port });
this.status = 'on';
cb();
});
},
close(cb = () => {}) {
if (this.http) {
this.http.close(() => {
this.status = 'off';
cb();
});
} else {
throw '❗️ cannot close server that has not been started ❗️';
}
},
};
return server;
};
usage (exactly the same, but in jest test body-parser isn't working properly):
import createServer from '../server'
let server = createServer()
server.listen(5050);
I'm using postman, post response outside of test:
{
"projects": [
{
"id": "test",
"active": true,
"timestamp": "2018-02-25T21:33:08.006Z"
},
{
"id": "TEST-PROJECT",
"active": true,
"timestamp": "2018-03-05T21:34:34.604Z"
},
{
"id": "asd",
"active": true,
"timestamp": "2018-03-06T23:29:55.348Z"
}
],
"total": 3
}
unexpected post response inside jest test server:
Error
UnsupportedMediaTypeError: unsupported charset "UTF-8" at /Users/awilmer/Projects/arranger/node_modules/body-parser/lib/read.js:83:18 at invokeCallback (/Users/awilmer/Projects/arranger/node_modules/raw-body/index.js:224:16) at _combinedTickCallback (internal/process/next_tick.js:131:7) at process._tickCallback (internal/process/next_tick.js:180:9)
So I was able to reproduce the issue and find the source of the issue and the workaround to make it work. The issue is caused by jest framework.
Before you jump on reading the rest of the thread, I would suggest you read another Jest thread I answer long back. This would help get some context internals about the require method in jest
Specify code to run before any Jest setup happens
Cause
The issue happens only in test and not in production. This is because of jest require method.
When you run your tests, it starts a express server, which calls the node_modules/raw-body/index.js as shown in below image
As you can see the encodings is null. This is because the iconv-lite module does a lazy loading of encodings. The encodings are only loaded when getCodec method gets executed.
Now when your test has fired the API, the server needs to read the body so the getCodec gets called
This then goes through the jest-runtime/build/index.js custom require method (which is overloaded if you read the previous link).
The execModule has a check for this._environment.global, which is blank in this case and hence a null value is returned and the module never gets executed
Now when you look at the exports of the encodings module, it just is a blank object
So the issue is purely a jest. A feature jest lacks or a bug mostly?
Related Issues
Related issues have already been discussed on below threads
https://github.com/facebook/jest/issues/2605
https://github.com/RubenVerborgh/N3.js/issues/120
https://github.com/sidorares/node-mysql2/issues/489#issuecomment-313374683
https://github.com/ashtuchkin/iconv-lite/issues/118
https://github.com/Jason-Rev/vscode-spell-checker/issues/159
Fix
The fix to the problem is that we load the module during our test itself and force a early loading instead of lazy loading. This can be done by adding a line to your index.test.js at the top
import encodings from '../../node_modules/iconv-lite/encodings';
import createServer from '#arranger/server';
After the change all the test pass, though you have a error in the url of the test so you get Cannot POST /
I'm adding a slightly different solution inspired from #Tarun Lalwani
Add the following lines at the top of your test file.
const encodings = require('./node_modules/iconv-lite/encodings');
const iconvLite = require('./node_modules/iconv-lite/lib');
iconvLite.getCodec('UTF-8');
I spent many hours trying to figure out why Jest would report a 415 error code when testing the Node.js server. Node.js is configured to use app.use(bodyParser.json(...)); on our system, too. That didn't solve the issue.
Solution
When using res.status(...), you MUST either chain on .json() or use res.json(), too. That means if you respond with a 500 error or otherwise and you don't return any JSON data, you still need to use res.json(). No idea why, as that defeats the whole purpose of app.use(bodyParser.json(...)); in the first place.
Example
const express = require('express');
const router = express.Router();
router.post("/register", (req, res) => {
// ...
res.status(500).json();
// ...
});

Run a gulp tasks on multiple sets of files

I have a gulp task that I would like to run on multiple sets of files. My problem is pretty much similar to what is described here except that I define my sets of files in an extra config.
What I've come up with so far looks like the following:
config.json
{
"files": {
"mainScript": [
"mainFileA.js",
"mainFileB.js"
],
"extraAdminScript": [
"extraFileA.js",
"extraFileB.js"
]
}
}
gulpfile.js
var config = require ('./config.json');
...
gulp.task('scripts', function() {
var features = [],
dest = (argv.production ? config.basePath.compile : config.basePath.build) + '/scripts/';
for(var feature in config.files) {
if(config.files.hasOwnProperty(feature)) {
features.push(gulp.src(config.files[feature])
.pipe(plumper({
errorHandler: onError
}))
.pipe(jshint(config.jshintOptions))
.pipe(jshint.reporter('jshint-stylish'))
.pipe(sourcemaps.init())
.pipe(concat(feature + '.js'))
.pipe(gulpif(argv.production, uglify()))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(dest))
);
}
}
return mergeStream(features);
});
My problem is that this doesn't seem to work. The streams are not combine or at least nothing really happens. Some while ago others ran into a similar problem, see here, but even though it should have been fixed it's not working for me.
By the way I've also tested merging the streams in this way:
return es.merge(features)
return es.merge.apply(null, features)
And if I just run the task on a single set of files it works fine.
Motivation
The reason why I want to do this is that at some point concatenating and minifying ALL scripts into one final file doesn't make sense when the sheer number of files is too large. Also, sometimes there is no need to load everything at once. For example all scripts related to an admin interface doesn't need to be load by every visitor.

How to generate multiple reports with mocha?

I want to have the following reports:
coverage
spec
xunit
all running in a single mocha execution from my grunt
Currently - I have to run the tests 3 times, each time to generate a different report(!).
So I use grunt-mocha-test with 2 configuration where only the reporter is different (once xunit-file and once spec).
And then I have grunt-mocha-istanbul that runs the tests yet again,and generates the coverage report.
I tried using
{
options: {
reporters : ['xunit-file', 'spec']
}
}
for grunt-mocha-test at least to bring it down to 2, but that doesn't work as well.
reading grunt-mocha-istanbul documentation, i can't seem to find any info about reporter configuration.
How can I resolve this?
Maybe this can help:
https://github.com/glenjamin/mocha-multi
AFAIK this is not supported in Mocha yet, but it is on its way:
https://github.com/mochajs/mocha/pull/1360
Hope this helps,
György
I ran into the same problem recently, and found nothing after looking around SO as well as GH issues. It seems that topic of officially supporting multiple reporters are getting postponed over and over.
Having said that having a custom solution is quite easy, assuming the reporters you want to combine already exist. What I did is to create a small and naive custom reporter, and used the reporter in .mocharc.js config.
// junit-spec-reporter.js
const mocha = require("mocha");
const JUnit = require("mocha-junit-reporter");
const Spec = mocha.reporters.Spec;
const Base = mocha.reporters.Base;
function JunitSpecReporter(runner, options) {
Base.call(this, runner, options);
this._junitReporter = new JUnit(runner, options);
this._specReporter = new Spec(runner, options);
return this;
}
JunitSpecReporter.prototype.__proto__ = Base.prototype;
module.exports = JunitSpecReporter;
// .mocharc.js
module.exports = {
reporter: './junit-spec-reporter.js',
reporterOptions: {
mochaFile: './tests-results/results.xml'
}
};
The example above shows how to use both spec and junit reporter.
More info on custom reporter: https://mochajs.org/api/tutorial-custom-reporter.html
Note that this is just a proof of concept and can be made prettier and more robust using more generic approach (and TypeScript).
Update 14.9.2021
I have created a utility package for this: https://www.npmjs.com/package/#netatwork/mocha-utils
For simultaneously reporting for spec and x-unit, there's also an NPM package called spec-xunit-file.
In grunt:
grunt.initConfig({
mochaTest: {
test: {
options: {
reporter: 'spec-xunit-file',
...
},
...
}
}
...
});

Resources