Sourcemaps in puppeteer? - node.js

I'm puppeteering (could this be a verb?) an app built with webpack and on page.on('error') I get errors like this:
TypeError: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_promise___default.a.map is not a function
at _default.<anonymous> (....../app.js:23858:89)
at Generator.next (<anonymous>)
at step (....../app.js:35041:30)
at ....../app.js:35052:13
Is there a way, and if so how could I get these errors correctly sourcemapped?
(I know for sure I'm generating the sourcemaps)

I did it in the following way... A bit delayed but someone might find it useful.
Once you have navigated to the page throwing the error you inject this lib: https://github.com/novocaine/sourcemapped-stacktrace
await page.addScriptTag({
url: 'https://cdn.jsdelivr.net/npm/sourcemapped-stacktrace#1.1.8/dist/sourcemapped-stacktrace.js',
});
Then you listen for the error events:
page.on('pageerror', logStackTrace);
page.on('error', logStackTrace);
In the logStackTrace function you extract the sourcemapped stacktrace as follows and log it:
const logStackTrace = async (error) => {
page.evaluate(stack => new Promise(resolve =>
window.sourceMappedStackTrace.mapStackTrace(stack, (newStack) => {
resolve(newStack);
})
), typeof error.stack === 'string' ? error.stack : error.stack.join('\n'))
.then((result) => {
console.log('ERROR:', error.message, result[0]);
});
};
It only correctly maps the first line of the stack for me though. Still infinitely more useful.

If you use one of the eval variants for your webpack devtool then Puppeteer should pick it up:
// webpack.config.js
module.exports = {
// ...
mode: 'development',
devtool: 'eval-source-map',
};

Related

Fastify plugin dependency - Registering MySQL before routes

I'm new to NodeJS and Fastify and I'm fairly sure I'm making a basic mistake here but I can't see what it is.
I have separated out my MySQL connection and routes into separate plugins. It appears that the MySQL plugin is not registering in time, leading to the routes not working.
I was under the impression that registering plugins was done asynchronously (loading one plugin at a time), but it can't seem to find "fastify.mysql.query".
Error:
"Cannot read properties of undefined (reading 'query')","stack":"TypeError: Cannot read properties of undefined (reading 'query')\n at Object.<anonymous> (/Users/dally/Projects/NodeJS/boodil-payments-api/routes/payments.js:4:23)\n at preHandlerCallback (/Users/dally/Projects/NodeJS/boodil-payments-api/node_modules/fastify/lib/handleRequest.js:126:28)\n at preValidationCallback (/Users/dally/Projects/NodeJS/boodil-payments-api/node_modules/fastify/lib/handleRequest.js:110:5)\n at handler (/Users/dally/Projects/NodeJS/boodil-payments-api/node_modules/fastify/lib/handleRequest.js:74:7)\n at handleRequest (/Users/dally/Projects/NodeJS/boodil-payments-api/node_modules/fastify/lib/handleRequest.js:22:5)\n at runPreParsing (/Users/dally/Projects/NodeJS/boodil-payments-api/node_modules/fastify/lib/route.js:487:5)\n at Object.routeHandler [as handler] (/Users/dally/Projects/NodeJS/boodil-payments-api/node_modules/fastify/lib/route.js:434:7)\n at Router.lookup (/Users/dally/Projects/NodeJS/boodil-payments-api/node_modules/find-my-way/index.js:368:14)\n at Server.emit (node:events:527:28)\n at parserOnIncoming (node:_http_server:956:12)"},"msg":"Cannot read properties of undefined (reading 'query')"}
Server.js
const fastify = require('fastify')({ logger: true })
fastify.register(require('./config/db'))
fastify.register(require('./routes/payments'))
const PORT = 2000
const start = async () => {
try {
await fastify.listen({ port: PORT })
}
catch (e) {
fastify.log.error(e)
process.exit(1)
}
}
start()
DB.js
module.exports = function (fastify, options, done) {
fastify.register(require('#fastify/mysql'), {
connectionString: 'mysql://root:password#localhost/boodil'
})
done()
}
payment.js
function paymentRoutes(fastify, opts, done) {
fastify.get('/get-transactions', (req, reply) => {
fastify.mysql.query(
'SELECT * FROM transactions',
function onResult(err, result) {
reply.send(err || result)
}
)
})
fastify.get('/get-transaction:id', (req, reply) => {
fastify.mysql.query(
'SELECT * FROM transactions where id = ?', [req.params.id],
function onResult(err, result) {
reply.send(err || result)
}
)
})
done()
}
module.exports = paymentRoutes
As said in comments, you need to use the fastify-plugin wrapper in your DB.js file.
To understand this error, you must get confident with fastify's encapsulation context.
It is worth reading these answers:
https://stackoverflow.com/a/61054534/3309466
https://stackoverflow.com/a/73586266/3309466
In practice: your database connection was isolated into a context that was unreachable by the paymentRoutes.
I'm coming from a PHP/Laravel background where this is quite standard
What fastify refuses is creating a global context where all you need is shared. Of course, you can do it, by it is not the default behaviour. You need to declare that the exported plugin has something that can be used by other contexts.

get read ENOTCONN error on windows 10 when calling a docker api

I' trying to start an exec instance of my docker image through my node project with code below:
return axios.post(host + `/exec/${id}/start`, { Detach: false, Tty: true },
{ responseType: 'stream' })
.then(function () {
let stream = response.data
let socket = stream.socket
socket.on('data', (data: string) => {
process.stdin.pause()
if (!firstLine)
process.stdout.write(data)
firstLine = false
process.stdin.resume()
})
process.stdout.on('data', i => {
socket.write(i.toString())
if (i == DetachKey) {
rl.emit('SIGINT')
}
})
rl.on('SIGINT', function () {
// stop input
socket.emit('end')
process.stdin.pause()
process.stdout.write(exec_exit_msg)
process.exit(0)
})
})
On Linux(Ubuntu & Mint) everything is OK, but when I test my code on windows 10 I get the error blow:
Error: read ENOTCONN
at WriteStream.Socket._read (net.js:529:20)
at WriteStream.Readable.read (_stream_readable.js:453:10)
at resume_ (_stream_readable.js:929:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
My node version is v10.15.3 (using nvm).
It seems that there is something wrong with the socket connection. I searched a lot but could not figured out how to solve the problem.
Below Part of Code makes the error. I removed it and change the code a little
But I don't know why it was ok in ubuntu
process.stdout.on('data', i => {
socket.write(i.toString())
if (i == DetachKey) {
rl.emit('SIGINT')
}
})

Protractor generates an error when disabling flow control to test an Angular app

I've been struggling with this error for a while and I'm running out of mana. I'm currently trying to test an Angular app with protractor and async/await. According to the doc, I have to disable the control flow by adding the following to my config file:
SELENIUM_PROMISE_MANAGER: false but doing so produces the following error:
UnhandledPromiseRejectionWarning: Error: Error while waiting for Protractor to sync with the page: "both angularJS testability and angular testability are undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See https://github.com/angular/protractor/issues/2643 for details" I visited the url (https://github.com/angular/protractor/issues/2643) but it didn't turn out very helpful.
At this point I'm not sure if I'm doing something wrong or if it's a bug with protractor itself. For this reason I also opened an issue on GitHub.
Here is my test:
import {
browser,
ExpectedConditions,
$
} from 'protractor';
describe('When user click \"Test\" button', async () => {
beforeAll(async () => {
expect(browser.getCurrentUrl()).toContain('myawesomewebsite');
});
it ("should click the button", async () => {
var button = $(".button");
button.click();
});
});
And here is my full configuration:
exports.config = {
capabilities: {
'browserName': 'chrome'
},
seleniumAddress: 'http://localhost:4444/wd/hub',
framework: 'jasmine',
specs: ['test.spec.ts'],
SELENIUM_PROMISE_MANAGER: false,
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
},
beforeLaunch: function () {
require('ts-node/register')
}
};
You missed await before each protractor api invoking.
describe('When user click \"Test\" button', async () => {
beforeAll(async () => {
expect(await browser.getCurrentUrl()).toContain('myawesomewebsite');
});
it ("should click the button", async () => {
var button = $(".button");
await button.click();
});
});
So, thanks to #CrispusDH on GitHub, I figured out that I could use waitForAngularEnabled in the configuration file and not just in the spec file. Using it in the spec file was not working, but if used in the onPrepare hook of the configuration file, the error goes away.
A lot of resources online were saying to set it to false, but this wasn't working for me as Protractor couldn't find element without waiting for Angular, so I did set it to false in the configuration and file but called browser.waitForAngularEnabled(true); in my specs file (beforeAll hook). Now the error is gone, allowing me to use async/await.
Here is the proper configuration to use:
SELENIUM_PROMISE_MANAGER: false,
onPrepare: async () => {
await browser.waitForAngularEnabled(false);
}
And here is the code to call in spec file:
beforeAll(async () => {
browser.waitForAngularEnabled(true);
});

How to test a function that throws an error asynchronously, using tape?

I am attempting to test this module (receiver.js) for an error thrown:
var request = require('request')
module.exports = function(url){
request({
url: url,
method: 'POST'
}, function(error) {
if(error){
throw error
}
})
}
using this test (test.js):
var test = require('tape')
test('Receiver test', function(t){
var receiver = require('./receiver')
t.throws(function(){
receiver('http://localhost:9999') // dummy url
}, Error, 'Should throw error with invalid URL')
t.end()
})
but tape runs the assertion before the error is thrown, resulting in the following error message:
TAP version 13
# Receiver test
not ok 1 Should throw error with invalid URL
---
operator: throws
expected: |-
[Function: Error]
actual: |-
undefined
at: Test.<anonymous> (/path/to/tape-async-error-test/test.js:5:4)
...
/path/to/receiver.js:9
throw error
^
Error: connect ECONNREFUSED 127.0.0.1:9999
at Object.exports._errnoException (util.js:856:11)
at exports._exceptionWithHostPort (util.js:879:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1062:14)
Is there a way around this?
Generally, using tape, you have to ensure you call assert.end() after the async call has completed. Using promises (would require request-promise and returning the promise):
test('Receiver test', function(t){
// Tells tape to expec a single assertion
t.plan(1);
receiver('http://localhost:9999')
.then(() => {
t.fail('request should not succeed')
})
.catch(err => {
t.ok(err, 'Got expected error');
})
.finally({
t.end();
});
});
Using async/await:
test('Receiver test', async function(t) {
try {
await receiver('http://localhost:9999');
assert.fail('Should not get here');
} catch (err) {
assert.ok(err, 'Got expected error');
}
t.end();
});
The above example is mostly correct but here's a complete working example that compares async to synchronous side by side and also shows how to check for the error message in a manner similar to the tape examples given on tape's README.md.
test('ensure async function can be tested to throw', async function(t) {
// t.throw works synchronously
function normalThrower() {
throw(new Error('an artificial synchronous error'));
};
t.throws(function () { normalThrower() }, /artificial/, 'should be able to test that a normal function throws an artificial error');
// you have to do this for async functions, you can't just insert async into t.throws
async function asyncThrower() {
throw(new Error('an artificial asynchronous error'));
};
try {
await asyncThrower();
t.fail('async thrower did not throw');
} catch (e) {
t.match(e.message,/asynchronous/, 'asynchronous error was thrown');
};
});

Is babel supposed to throw syntax errors?

I have setup a node.js gulp file to use the babel transform allowing me to utilize ES6 features in browser scripts.
When I add an intentional syntax error to the input file I am not seeing any error messages being output by babelify despite having subscribed to the 'error' event as suggested in the Using Babel with Browserify guide.
Example of invalid syntax:
immmmport $ from 'jquery';
Instead of showing an error in the CLI it just silently fails.
Here is how I have configured my gulp task:
gulp.task('build', () => {
browserify(options)
.transform(babelify)
.add(sourceFilePath)
.bundle()
.pipe(source(outputFileName))
.pipe(buffer())
.pipe(gulpif(!argv.production, sourcemaps.init({ loadMaps: true })))
.pipe(gulpif(argv.production, streamify(uglify())))
.pipe(gulpif(!argv.production, sourcemaps.write('/')))
.pipe(gulp.dest(outputDirName));
})
Am I missing any steps here?
The source of this problem was that I needed to add the following line:
.on("error", err => { gutil.log("Browserify Error", gutil.colors.red(err.message)) })
So that I have this:
gulp.task('build', () => {
browserify(options)
.transform(babelify)
.add(sourceFilePath)
.bundle()
.on("error", err => { gutil.log("Browserify Error", gutil.colors.red(err.message)) })
.pipe(source(outputFileName))
.pipe(buffer())
.pipe(gulpif(!argv.production, sourcemaps.init({ loadMaps: true })))
.pipe(gulpif(argv.production, streamify(uglify())))
.pipe(gulpif(!argv.production, sourcemaps.write('/')))
.pipe(gulp.dest(outputDirName));
})
The following page was useful in diagnosing this issue:
https://github.com/substack/node-browserify/issues/1044

Resources