Having trouble importing an async function into another file - node.js

I've been working on a Guilded Bot that automatically runs a function after x amount of MS. My goal is to automate this function to check a website for new posts. The issue I'm encountering is when trying to import the function and call on it within another file. None of the recommended methods I've found seem to work. Below is my code.
//relay.ts under ./automations/
async function patchNotes(message:Message) {
}
export { patchNotes }
//The main file in src its called index.ts
import path from "path";
import { BotClient, Client, Message } from "#guildedjs/gil";
const { token, token2 } = require('./config.json');
import { patchNotes } from './automations/relay';
const client = new BotClient({
token: token,
prefix: "/",
});
client.once('ready', () => console.log('Ready! Shut down using "ctrl+c"'));
client.login();
process.on("unhandledRejection", console.log)
//setTimeout(() => console.log(client.commands), 600);
// Automations
patchNotes
setInterval(() => patchNotes, 6000);
Currently, this method doesn't return console errors for both Types and other stuff. But it also doesn't run the code at all? I've tried other methods too but none have worked so far. Below are what packages I'm using.
ts-node "10.8.1"
typescript "4.7.4"
It's running Node.js and all files are written in TS. If you need any more details, I'd be happy to give them. Really hoping to get past this issue instead of just putting the function in my main file.

So I've actually just found the answer. So it seems I can use setInterval with async tasks. Below is the code I use to achieve this.
setInterval(async () => {
await function();
}, delay)
As for my other issue. I've figured out that I could just write client.messages.send instead of putting message. in front of it. Reason I didn't follow the advice of the recent comment is because this function shouldn't have any values returning. The reason I added message: Message is because there is a line in my code that uses "message". Which is the one mentioned above. Shoulda added that to this thread. Thanks for the response though. Resolved.

Related

nockBack fails to record any fixtures

I cannot get nockBack to record any fixtures, although it should do that. My test code looks as follows:
describe("#searchForProjects", function () {
beforeEach(function () {
nock.back.setMode("record");
this.con = getTestConnection(ApiType.Production);
});
it("finds a home project", async function () {
const { nockDone, context } = await nock.back("search_for_project.json");
await searchForProjects(this.con, "home:dancermak", {
idOnly: true,
exactMatch: true
}).should.eventually.deep.equal([
{
name: "home:dancermak",
apiUrl: normalizeUrl(ApiType.Production)
}
]);
nockDone();
});
});
Just running this specific test results in a NetConnectNotAllowedError: Nock: Disallowed net connect for $URL.
I have tried including a nock.restore() before the whole test, which results in the request going through, but nock doesn't bother recording anything.
The underlying code is using the https module from nodejs, so that shouldn't be a problem?
Any help would be greatly appreciated.
I have finally managed to crack this and the solution is embarrassingly simple: recording must be activated before creating any http(s).request calls. In my case it was obscured a bit as I have a class that on construction saves either http.request or https.request in a member variable. Activating the recorder beforehands solves the issue.
For me the problem was that I had a failing assertion in my test. This meant that nockDone was not called and so no nocks were written to disk.

Jest Cannot read property 'createEvent' of null

I was trying to mock rejected value and got this error. It's weird that this construction works in the case of "success" addUser.mockImplementation(value => jest.fn().mockResolvedValue(value)), but when I'm trying to do the same trick with rejecting, it doesn't work and says 'Cannot read property 'createEvent' of null'
Here is my test case
it('receives invalid value and throws an error', async () => {
addUser.mockImplementation(() =>
jest.fn().mockRejectedValue(new Error('Sample error'))
)
const enqueueSnackbar = jest.fn()
useSnackbar.mockReturnValue({ enqueueSnackbar })
const { emailInput, form, submitButton } = setup()
await act(async () => {
fillIn(emailInput, 'sample#mail.com')
})
expect(emailInput.value).toBe('sample#mail.com')
expect(submitButton).toHaveProperty('disabled', false)
await act(async () => {
fireEvent.submit(form)
})
expect(enqueueSnackbar).toHaveBeenCalledTimes(1)
expect(enqueueSnackbar).toHaveBeenCalledWith(`Sample error`, {
variant: 'error'
})})
Does anyone know how to make it work?
This seems to be the #1 question that is found when someone Googles "Cannot read property 'createEvent' of null", so leaving this answer here for those readers:
For me this error came in the midst of a test.
When executing a series of tests, some test or the other used to fail with this error, with no indication of where things went wrong. But the answer turned out to be not the test but the component itself:
It was an unmocked API call.
There was an API call being made in a hook and that hook was used in the component with the failing tests. Obviously Jest cleaned up everything after completing its test, and when the call returned, it found nothing so it errored out.
Mocking the hook solved the issue.
If someone comes across such an error, make sure to mock any asynchronous logic you have, especially if it interacts with the DOM when it returns.
Similar to what #alexandre_anicio stated. I was getting this error when using the findAllByText query.
expect(screen.findAllByText('word'))...
When I switched to the getAllByText the error went away and the test passed.
expect(screen.getAllByText('word'))...
If I used expect(await screen.findAllByText('word'))... I noticed the test passed as well.
Digging deeper, this is because findBy tests return a promise so the await is needed. https://testing-library.com/docs/guide-disappearance/#1-using-findby-queries
It would have been nice for the library to throw a better error however.
This seems to work for me but I can't explain it. Try removing your act() wrapper, and use await immediately after calling the fireEvent function.
fireEvent.submit(form);
await wait();
When I encountered this same error message, I discovered I had forgotten to declare my test function as async after I updated the expectation to include await.
waitFor already uses act under the hood so there's no need to use the act blocks there.
I recognize the error you mentioned but the way I replicate it is using waitFor without await, something like this:
it('works', async() => {
render(<SomeComponent />);
// (some setup here)
waitFor(() => { // notice that we are not awaiting this promise
expect(someChange).toBeInTheDocument();
});
});
Could you try
it('receives invalid value and throws an error', async () => {
addUser.mockImplementation(() =>
jest.fn().mockRejectedValue(new Error('Sample error'))
)
const enqueueSnackbar = jest.fn()
useSnackbar.mockReturnValue({ enqueueSnackbar })
const { emailInput, form, submitButton } = setup()
fillIn(emailInput, 'sample#mail.com') // This is using some fireEvent under the hood right?
await waitFor(() => {
expect(emailInput.value).toBe('sample#mail.com')
expect(submitButton).toHaveProperty('disabled', false)
});
fireEvent.submit(form)
await waitFor(() => {
expect(enqueueSnackbar).toHaveBeenCalledTimes(1)
expect(enqueueSnackbar).toHaveBeenCalledWith(`Sample error`, {
variant: 'error'
})
});
})
Similar issue and error messages, adding await before userEvent did the trick
Before
userEvent.upload(screen.getByRole('button'), ...)
userEvent.upload(screen.getByLabelText('Upload'), FILE)
After
await userEvent.upload(screen.getByRole('button'), ...)
await userEvent.upload(screen.getByLabelText('Upload'), FILE)
I had some problems using mockImplementation(() => Promise) (returning some promise) and the await waitFor(()=> ...) at the same time.
If you are using react-testing-library, you can work around this problem using findBy query, that are a combination of getBy queries and waitFor. The only downside is that you have to find something visual (a text, data-test-id, label, etc...) that can tell you that the mock function have been called. On your code you can try something like this:
it('receives invalid value and throws an error', async () => {
addUser.mockImplementation(() =>
jest.fn().mockRejectedValue(new Error('Sample error'))
)
await screen.findByText('Sample Error message reflected in your component')
... rest of your tests ...
})
1. await waitFor(() => expect(history.location.pathname).toBe('/auth'))
2. await waitFor(() => expect(history.location.pathname)).toBe('/auth')
It's about something else but the same error. Spent 2 hours so you don't have to :)
The second one with the parenthesis in the wrong place was the culprit
I was getting
/…/node_modules/.pnpm/react-dom#17.0.2_react#17.0.2/node_modules/react-dom/cjs/react-dom.development.js:3905
var evt = document.createEvent('Event');
^
TypeError: Cannot read property 'createEvent' of null
at Object.invokeGuardedCallbackDev (/…/node_modules/.pnpm/react-dom#17.0.2_react#17.0.2/node_modules/react-dom/cjs/react-dom.development.js:3905:26)
at invokeGuardedCallback (/…/node_modules/.pnpm/react-dom#17.0.2_react#17.0.2/node_modules/react-dom/cjs/react-dom.development.js:4056:31)
at flushPassiveEffectsImpl (/…/node_modules/.pnpm/react-dom#17.0.2_react#17.0.2/node_modules/react-dom/cjs/react-dom.development.js:23543:11)
at unstable_runWithPriority (/…/node_modules/.pnpm/scheduler#0.20.2/node_modules/scheduler/cjs/scheduler.development.js:468:12)
at runWithPriority$1 (/…/node_modules/.pnpm/react-dom#17.0.2_react#17.0.2/node_modules/react-dom/cjs/react-dom.development.js:11276:10)
at flushPassiveEffects (/…/node_modules/.pnpm/react-dom#17.0.2_react#17.0.2/node_modules/react-dom/cjs/react-dom.development.js:23447:14)
at Object.<anonymous>.flushWork (/…/node_modules/.pnpm/react-dom#17.0.2_react#17.0.2/node_modules/react-dom/cjs/react-dom-test-utils.development.js:992:10)
at Immediate.<anonymous> (/…/node_modules/.pnpm/react-dom#17.0.2_react#17.0.2/node_modules/react-dom/cjs/react-dom-test-utils.development.js:1003:11)
at processImmediate (internal/timers.js:461:21)
Tracked down to an import statement.
I was able to "fix" it by changing:
import { AddCircle, RemoveCircle } from '#mui/icons-material';
to
import AddCircle from '#mui/icons-material/AddCircle';
import RemoveCircle from '#mui/icons-material/RemoveCircle';
Crazy.
If the error is because of wrong usage of jest's findBy* instead of async/await you can also return promise:
it('test', () => {
expect.assertions(1);
return screen
.findByTestId('iiError')
.then(elem =>
expect(elem).toHaveTextContent(
"This is error message"
)
);
});
Do not forget about expect.assertions and return!
Ref: https://jestjs.io/docs/tutorial-async
I had the same issue, the culprit in my case was that the rendered React component was unmounted with .unmount(). A running API call triggered a callback and React tried to update the DOM, that was already unmounted.
Since this is the top result on Google for this issue, I'll add my own answer. For me, this issue was happening on Circle CI when my tests were running. The Circle CI server ran the tests more slowly because it's CPU is limited, so a long-running test with lots of userEvent.types in it was exceeding the default timeout of 5 seconds.
I don't know why it didn't give an error about exceeding the timeout, but this thread helped me track it down.
All I had to do was increase the timeout on the long-running test.
Error occurred for me because I had work scheduled from useEffect that resolved after the rest was torn down.
The solution is to await Promise.sleep() after each test.
I was facing the same issue
It had to something with the async function not completing before the test case completes
I solved this using await flushMicrotasksQueue()
in my code

How do I go "async all the way down" with Node Express?

I have many async functions in my system, so I need to go "async all the way down", which is to the point where the http.Server and express.Application app are created.
(This is unavoidable in an async system - there will be many async routines which are needed in constructors, which cannot be done, and so we need to use async factory functions instead, which lead to async creep all the way down to the entry point.)
But I'm not sure of the Node/TypeScript syntax to use to bootstrap the app.
My main entry point is System.ts:
class default export System {
public constructor() {
// init Express.Application
// init http.Server
// init other parts of the system
}
public async start(): Promise<void> {
// start the system asynchronously
// start listening with http.Server
}
}
Then I have a bootstrapping module Main.ts:
import System from "./System"
const system = new System();
export default ???; // PROBLEM IS HERE
Which should be run:
node ./dist/Main.js
But I'm not sure what to use in the export line. I tried all these:
export default await system.start(); // doesn't compile (obviously)
export default system.start(); // doesn't seem right
export default system.start().then(); // this works *maybe*
The last line works based on a smoke test - but I'm not sure if that's the way to do it, and whether there's something down the line that may fail.
What is the canonical way to start an asynchronous node app?
UPDATE
Based on #JacobGillespie's answer, the Main.ts bootstrapping module is now:
import System from "./System"
new System().start().then();
//new System().start().catch(e => console.error(e)); // alternative
In my case, System.ts has handlers for errors and unhandled promises, and does logging (otherwise use the "alternative" line). So the bootstrapping module just bootstraps the system.
async / await here are operating on promises, so you essentially want to "start" the promise by calling .then or .catch.
My go-to snippet for this is creating an async run or main function, then attaching error handling to the process, something like this:
async function run() {
// run the app, you can await stuff in here
}
run().catch(err => {
console.error(err.stack)
process.exit(1)
})
In your case that would look like (Main.ts):
import System from "./System"
async function run() {
const system = new System()
await system.start()
}
run().catch(err => {
console.error(err.stack)
process.exit(1)
})
You don't need to export anything since this module file isn't being imported anywhere else (it's the entry file).
You can just call system.then() or system.catch(), but personally I like the async function run() pattern since you may need to coordinate more than one async thing in the future and this makes the code more explicit.
system.start().then() => {
value => export default value
}
In my opinion, a better way would be:
System.ts:
function System():Promise<string>{
//setup express and the server
return new Promise((res,rej) => {
//the server var is just the http server instance
server.listen(8000,() => resolve("server created"));
});
}
export {System}
And then in Main.ts:
import {System} from "yourpath"
And then:
System().then(() => {
//code runs when server is created
}).catch(err => console.error(err));

Why does the selenium function elementLocated in node.js throw an error only on Jenkins?

I'm using the function driver.wait(until.elementLocated()) below, written with node.js, as an explicit wait on my Selenium tests to ensure that the pages in my test load properly. When I run the tests from my local CLI they work perfectly, headlessly and with GUI.
const loadMyPage = {
loadThePage: async function(driver) {
try {
await driver.wait(
until.elementLocated(
By.css("div.radio-select span:nth-child(7)")
),
20000
);
} catch (e) {
console.trace(loadMyPage.loadThePage);
throw e;
}
}
However, when I run the tests in Jenkins headlessly I receive the following error every time I use the function elementLocated().
TypeError: Wait condition must be a promise-like object, function, or a Condition object[0m[90m
at Driver.wait (node_modules/selenium-webdriver/lib/webdriver.js:928:13)
at Object.loadMyPage (foobar-tests/page.js:35:20)
at Context.<anonymous> (foobar-tests/foobar.test.js:32:30)
at <anonymous>
Is there anything specific that could cause this error in Jenkins? I have managed to narrow it down to this specific function, elementLocated().
I was able to find a workaround for my issue, however it appears that there is a larger issue at play with selenium. More information on the core issue can be found at https://github.com/SeleniumHQ/selenium/issues/5560.
I updated my explicit wait by passing an additional async function, and this cleared up the problem entirely in Jenkins. Example is below.
loadMyPage: async () => {
//The second async is necessary to run explicit wait functions like the one below.
//This issue is specific to selenium, more information can be found at https://github.com/SeleniumHQ/selenium/issues/5560.
loadThePage: async () {
try {
async driver =>
await driver.wait(
until.elementLocated(
By.css("div.radio-select span:nth-child(7)")
),
10000
);
} catch (e) {
console.trace(loadMyPage.loadThePage);
throw e;
}
}

Download files asynchrounsly and parse them synchronously with Node JS

I have a gulp task that downloads a few JSON files from GitHub, then prompts the user for values to replace in those files. For example, I have an .ftpconfig that gets download, and then the user is asked to enter hostname, username, password, and path.
Because the file first needs to be downloaded before it can be configured, and each file needs to be configured sequentially, I'm using quite a few nested callbacks. I'd like to change this "callback hell" system so that it utilizes async/await and/or promises instead, but I'm having a lot of difficulty understanding exactly why my code isn't working; it seems that promises fire their .then() functions asynchronously, which doesn't make sense to me.
My goals are as follows:
Download all config files asynchronously
Wait for all config files to finish downloading
Read existing settings from the config files
Prompt the user for changed settings in each config file synchronously
I've tried a number of approaches, none of which worked. I discarded the code I've used, but here's a rough recreation of the things I've tried:
Attempt #1:
return new Promise((resolve) => {
// download files...
}).then((resolve) => {
// configure first file...
}).then((resolve) => {
// configure second file...
}).then((resolve) => {
// configure thrid file...
});
Attempt #2:
const CONFIG_FILES = async () => {
const bs_download = await generate_config("browsersync");
const ftp_download = await generate_config("ftp");
const rsync_download = await generate_config("rsync");
return new Promise(() => {
configure_json("browsersync");
}).then(() => {
configure_json("ftp");
}).then(() => {
configure_json("rsync");
});
};
I'm sure I'm doing something very obviously wrong, but I'm not adapt enough at JavaScript to see the problem. Any help would be great appreciated.
My gulp task can be found here:
gulpfile.js
gulp-tasks/config.js
Thanks to #EricB, I was able to figure out what I was doing wrong. It was mostly a matter of making my functions return promises as well.
https://github.com/JacobDB/new-site/blob/d119b8b3c22aa7855791ab6b0ff3c2e33988b4b2/gulp-tasks/config.js

Resources