Refactoring probot event functions into seperate file causes error: TypeError: handler is not a function - javascript

I have the vanilla probot event function from the docs that comments on new issues:
const probotApp = app => {
app.on("issues.opened", async context => {
const params = context.issue({ body: "Hello World!" });
return context.github.issues.createComment(params);
});
}
This works fine.
I refactor the code into a separate file:
index.js
const { createComment } = require("./src/event/probot.event");
const probotApp = app => {
app.on("issues.opened", createComment);
}
probot.event.js
module.exports.createComment = async context => {
const params = context.issue({ body: "Hello World!" });
return context.github.issues.createComment(params);
};
But I receive this error:
ERROR (event): handler is not a function
TypeError: handler is not a function
at C:\Users\User\probot\node_modules\#octokit\webhooks\dist-node\index.js:103:14
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Promise.all (index 0)
When I create a test as recommended in the docs with a fixture and mock the event webhook call with nock this works fine. But when I create a real issue on GitHub this error is thrown.
How can I refactor the code into a separate file without causing the error?

This was my mistake.
This is the whole probot.event.js file:
module.exports.createComment = async context => {
const params = context.issue({ body: "Hello World!" });
return context.github.issues.createComment(params);
};
module.exports = app => {
// some other event definitions
}
By defining module.exports = app I overwrote the previous module.export. The createComment function was therefore never exported.
Removing module.exports = app = { ... } fixed it!

Related

NodeJS: My jest spyOn function is not being called

I don't understand why my spy is not being used. I have used this code elsewhere and it has worked fine.
Here is my test:
const {DocumentEngine} = require('../documentEngine')
const fileUtils = require('../utils/fileUtils')
const request = {...}
const fieldConfig = {...}
test('If the Carbone addons file is not found, context is built with the carboneAddons property as an empty object', async () => {
const expectedResult = {
carboneAddons: {},
}
const fileExistSpy = jest
.spyOn(fileUtils, 'checkFileExists')
.mockResolvedValue(false)
const result = await docEngine.buildContext(request, fieldConfig)
expect(fileExistSpy).toHaveBeenCalledTimes(1)
})
Here is the code that it is being tested:
async function buildContextForLocalResources(request, fieldConfig) {
/* other code */
const addonFormatters = await getCarboneAddonFormatters()
const context = {
sourceJson,
addonFormatters,
documentFormat,
documentTemplateId,
documentTemplateFile,
responseType,
jsonTransformContext
}
return context
}
async function getCarboneAddonFormatters() {
const addOnPath = path.resolve(
docConfig.DOC_GEN_RESOURCE_LOCATION,
'library/addon-formatters.js'
)
if (await checkFileExists(addOnPath)) {
logger.info('Formatters found and are being used')
const {formatters} = require(addOnPath)
return formatters
}
logger.info('No formatters were found')
return {}
}
This is the code from my fileUtils file:
const fs = require('fs/promises')
async function checkFileExists(filePath) {
try {
await fs.stat(filePath)
return true
} catch (e) {
return false
}
}
My DocumentEngine class calls the buildContext function which in turn calls the its method getCarboneAddonFormatters. The fileUtils is outside of DocumentEngine class in a utilities folder. The original code I had this working on was TypeScript as opposed to this which is just NodeJS Javascript. The config files for both are the same. When I try to step through the code (VSCode debugger), as soon as I hit the line with await fs.stat(filePath) in the checkFileExists function, it kicks me out of the test and moves on to the next test - no error messages or warnings.
I've spent most of the day trying to figure this out. I don't think I need to do an instance wrapper for the documentEngine, because checkFileExists is not a class member, and that looks like a React thing...
Any help in getting this to work would be appreciated.

Module.exports in JavaScript

What is different between module.exports = testMethod ; and module.exports = { testMethod } ; Because when I am using module.exports = testMethod ; it is throwing error as below.
Error: Route.get() requires a callback function but got a [object Undefined]
But I am okay with module.exports = { testMethod } ;
Whole codes are
const testMethod = asyncErrorWrapper(async (req, res, next) => {
const information = req.body;
const question = await Question.create({
title: information.title,
content: information.content,
user: req.user.id,
});
res.status(200).json({
success: true,
data: question,
});
});
module.exports = { testMethod };
From VSCode, change between the ES5 or ES6 version to Js can take you on a bad way.
So, be carreful, i have the same problem recently, and after refactoring by use on ES6 module.exports = router to the end of some Js file (Node project using express) it was done.
Strange for me, on Cloud9 on Aws i have no problems.
Both are worked to export your module to outside function. But when you are using any callback function with
module.exports = somectrl
then it will fail but
module.exports = { somectrl }
because when you create an object, it actually instanciate it but when you pass a ref function/ const function name then it will behave as a existing function which does not work right.
you can do something like this to work,
module.exports = somectrl()
or
module.exports = new somectrl()

Mocking node_modules which return a function with Jest?

I am writing a typeScript program which hits an external API. In the process of writing tests for this program, I have been unable to correctly mock-out the dependency on the external API in a way that allows me to inspect the values passed to the API itself.
A simplified version of my code that hits the API is as follows:
const api = require("api-name")();
export class DataManager {
setup_api = async () => {
const email = "email#website.ext";
const password = "password";
try {
return api.login(email, password);
} catch (err) {
throw new Error("Failure to log in: " + err);
}
};
My test logic is as follows:
jest.mock("api-name", () => () => {
return {
login: jest.fn().mockImplementation(() => {
return "200 - OK. Log in successful.";
}),
};
});
import { DataManager } from "../../core/dataManager";
const api = require("api-name")();
describe("DataManager.setup_api", () => {
it("should login to API with correct parameters", async () => {
//Arrange
let manager: DataManager = new DataManager();
//Act
const result = await manager.setup_api();
//Assert
expect(result).toEqual("200 - OK. Log in successful.");
expect(api.login).toHaveBeenCalledTimes(1);
});
});
What I find perplexing is that the test assertion which fails is only expect(api.login).toHaveBeenCalledTimes(1). Which means the API is being mocked, but I don't have access to the original mock. I think this is because the opening line of my test logic is replacing login with a NEW jest.fn() when called. Whether or not that's true, I don't know how to prevent it or to get access to the mock function-which I want to do because I am more concerned with the function being called with the correct values than it returning something specific.
I think my difficulty in mocking this library has to do with the way it's imported: const api = require("api-name")(); where I have to include an opening and closing parenthesis after the require statement. But I don't entirely know what that means, or what the implications of it are re:testing.
I came across an answer in this issue thread for ts-jest. Apparently, ts-jest does NOT "hoist" variables which follow the naming pattern mock*, as regular jest does. As a result, when you try to instantiate a named mock variable before using the factory parameter for jest.mock(), you get an error that you cannot access the mock variable before initialization.
Per the previously mentioned thread, the jest.doMock() method works in the same way as jest.mock(), save for the fact that it is not "hoisted" to the top of the file. Thus, you can create variables prior to mocking out the library.
Thus, a working solution is as follows:
const mockLogin = jest.fn().mockImplementation(() => {
return "Mock Login Method Called";
});
jest.doMock("api-name", () => () => {
return {
login: mockLogin,
};
});
import { DataManager } from "../../core/dataManager";
describe("DataManager.setup_api", () => {
it("should login to API with correct parameters", async () => {
//Arrange
let manager: DataManager = new DataManager();
//Act
const result = await manager.setup_api();
//Assert
expect(result).toEqual("Mock Login Method Called");
expect(mockLogin).toHaveBeenCalledWith("email#website.ext", "password");
});
});
Again, this is really only relevant when using ts-jest, as using babel to transform your jest typescript tests WILL support the correct hoisting behavior. This is subject to change in the future, with updates to ts-jest, but the jest.doMock() workaround seems good enough for the time being.

E/configParser - ReferenceError: element is not defined

I want to implement page object and to be more specific want to read element declared in function in it's prototype.
Tried different ways and those are working fine, however curious if implementation is possible this way also.
Spec File
var login_page = require('../pages/login_page');
describe('login page and properties', function () {
it('verify application launch with URL', function () {
browser.get('appUrl').then(function () {
browser.getTitle().then(function (appTitle) {
expect(appTitle).toBe('Protractor practice website');
});
});
login_page.enterUsername();
});
});
login_page.js:
var login_page = function() {
this.username = element(by.id('username'));
};
login_page.prototype.enterUsername = function() {
this.username.sendKeys('anyString');
};
module.exports = new login_page();
Error:
> protractor conf.js
[22:05:42] E/configParser - Error code: 105
[22:05:42] E/configParser - Error message: failed loading configuration file conf.js
[22:05:42] E/configParser - ReferenceError: element is not defined
Async / await
Think about using async / await. You'll need to change your config SELENIUM_PROMISE_MANAGER: false. This will unchain all the thenables.
Element not defined
Why is element not defined? Probably because the module is loaded before the global object element is defined. Consider instead creating the new object in a beforeAll so we know that element does exist.
Fixes with async / await and element not defined
Spec File:
const LoginPage = require('../pages/login_page').LoginPage;
let login_page = null;
describe('login page and properties', () => {
beforeAll(() => {
login_page = new LoginPage();
});
it('verify application launch with URL', async () => {
await browser.get('appUrl');
const appTitle = await browser.getTitle();
expect(appTitle).toBe('Protractor practice website');
await login_page.enterUsername();
});
});
login_page.js:
If you use JavaScript classes, you don't have to use prototypes.
export class LoginPage {
username = element(by.id('username'));
/**
* enter the user name.
* returns the promise to send keys
*/
async enterUsername() {
return this.username.sendKeys('anyString');
}
}

web worker onmessage - Uncaught SyntaxError: Unexpected token <

Following this example I wrote the following function:
var rasterToVectorWorker = new Worker(
"../../../services/canvas/rasterToVectorWorker.js"
);
rasterToVectorWorker.postMessage("message");
rasterToVectorWorker.onmessage = e => {
console.log("this is the return from the worker: ", e);
};
rasterToVectorWorker.js:
onmessage = function(e) {
console.log("Message received from main script");
var workerResult = "Result: " + e;
console.log("Posting message back to main script");
postMessage(workerResult);
};
But I am getting the following error message:
rasterToVectorWorker.js:1 Uncaught SyntaxError: Unexpected token <
Using window.onmessage did not solve the problem as well.
EDIT: I am using create-react-app without ejecting and adding webpack loaders
What am I doing wrong?
I guess you can try one of those options:
The first one is to put your rasterToVectorWorker inside public folder, then your WebWorker may be loaded properly.
const rasterToVectorWorker = new Worker('rasterToVectorWorker.js');
The another option is to load your WebWorker "dynamically":
import rasterToVectorWorker from '../../../services/canvas/rasterToVectorWorker.js'
function loadWebWorker(worker) {
const code = worker.toString();
const blob = new Blob(['('+code+')()']);
return new Worker(URL.createObjectURL(blob));
}
const rasterToVectorWorker = loadWebWorker(rasterToVectorWorker);
rasterToVectorWorker.postMessage("message");
I also had the same problem with a React project when I was trying to load the web worker file and pass it to my new worker.
While I have not used the fix in create-react-app, the solution should be similar.
I found I needed to use the web worker loader, which can be found here: https://www.npmjs.com/package/worker-loader
I then updated my webpack config to inline the worker like so:
{
test: /\.worker\.js$/,
use: {
loader: 'worker-loader',
options: {
inline: true
}
}
},
This then let me use my web worker.
A solution to use web workers with create-react-app without using worker-loader is https://github.com/alewin/useWorker.
Example:
import React from "react";
import { useWorker } from "#koale/useworker";
const rasterToVector = () => {
...
}
const Example = () => {
const [rasterToVectorWorker] = useWorker(rasterToVector);
const run = async () => {
const result = await rasterToVectorWorker();
console.log("End.");
};
return (
<button type="button" onClick={run}>
Run rasterToVectorWorker
</button>
);
};

Categories

Resources