How to detect a alert in puppeteer - node.js

How can I detect if after a navigation the page shows an alert with some message.
Can puppeteer detect or identify if the page has shown an alert box.
I tried
page.on('popup', ()=> {
console.log('popup detected');
});
also,
page.on('dialog', ()=> {
console.log('popup detected');
});

As said in the comments: If you register the event handlers (your code) after calling page.goto the event is already triggered before you are listening for the event.
Therefore, make sure to call page.goto after your code.

Here are my two cents :
private async gotoPage(path):Promise<Response|Result> {
return new Promise(async(resolve, reject) => {
await Promise.all([
this.page.on('dialog', async (dialog) => {
await dialog.dismiss().catch(() => {
console.log(dialog.message());
return new Result(TestStatus.FAIL, dialog.message());
});
}),
this.page.goto(`${this.baseURL}${path}`),
this.page.waitForNavigation({ waitUntil: 'load' })]).then(
async () => {
resolve(new Result(TestStatus.PASS, `Loading to path success : ${path}`));
},
async () => {
reject(new Result(TestStatus.FAIL, `Could not GotoPage : ${path}`));
});
});
}

Related

NodeJS await continues execution without resolved value

There are other questions similar to this one but not a single one of them helped me visualize my mistake.
I have this code:
function calc() {
return new Promise(resolve => {
setTimeout(() => {
resolve('block finished');
}, 5000);
});
}
async function asyncBlock() {
let result = await calc();
console.log('Result: ' + result);
return result;
}
app.get('/block', (req, res) => {
let result = asyncBlock();
console.log('Already returned the response without the result.');
res.send(result);
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
The execution continues without the await response, giving me this output:
Example app listening on port 3000
Already returned the response without the result.
Result: block finished
Mozilla documentations states that
If a Promise is passed to an await expression, it waits for the
Promise to be fulfilled and returns the fulfilled value.
Mozilla Doc
Your call to AsyncBlock isn't asynchronous.
Try this:
app.get('/block', async (req, res) => {
let result = await asyncBlock();
console.log('Waited for the result');
res.send(result);
})

Why does Promise.race not resolve in kafkajs eachMessage callback

I have defined a promise like this ...
const result = await Promise.race([
new Promise(resolve => {
consumer.run({
eachMessage: ({ message }) => {
const data = JSON.parse(message.value.toString());
if (data.payload.template
&& data.payload.template.id === '...'
&& data.payload.to[0].email === email) {
console.log('Should resolve!')
resolve(data.payload.template.variables.link);
console.log('resolved');
consumer.pause();
consumer.disconnect();
}
},
});
}),
new Promise((_, reject) => setTimeout(reject, 3000))
]);
console.log('result is ', result);
return result;
I can get to resolved but it doesnt print the result at the end, it seems like neither did the timeout nor the actual promise work as expected? Why is that? I suspect its something to do with using resolve inside the kafka js callback?
UPDATE: Seems like its Promise.race() thats not resolving, but why?
My suspicion is that your "success-side" promise inadvertently throws and you're swallowing the error silently.
Using a mock-up minimal implementation of the consumer (that succeeds or fails 50/50), the following code works.
Run the code sample a couple of times to see both cases.
var consumer = {
interval: null,
counter: 0,
run: function (config) {
this.interval = setInterval(() => {
this.counter++;
console.log(`Consumer: message #${this.counter}`);
config.eachMessage({message: this.counter});
}, 250);
},
pause: function () {
console.log('Consumer: paused');
clearInterval(this.interval);
},
disconnect: function () {
console.log('Consumer: disconnected');
}
};
Promise.race([
new Promise(resolve => {
const expectedMsg = Math.random() < 0.5 ? 3 : 4;
consumer.run({
eachMessage: ({ message }) => {
if (message === expectedMsg) resolve("success");
}
});
}),
new Promise((_, reject) => setTimeout(() => {
reject('timeout');
consumer.pause();
consumer.disconnect();
}, 1000))
]).then((result) => {
console.log(`Result: ${result}`);
}).catch((err) => {
console.log(`ERROR: ${err}`);
});
I have also moved consumer.pause() and consumer.disconnect() to the "timeout-side" promise, this way the consumer is guaranteed to disconnect, albeit it might run a tiny bit longer than necessary in the success case.

Jest how to test express API POST request?

I need to test if my POST request to my endpoint works properly with a Jest test. I had the idea of first getting the count of my Services table (I'm using sequelize orm), then to send a new post request and to finally get the new count and compare if the old count + 1 will equal to the new count, if true then the POST request works just fine.
test('Create a valid Service', async (done) => {
const service = {
name: "cool",
description: "description"
};
await Service.count().then(async function (count) {
await request(app)
.post('/api/services')
.send(service)
.then(async () => {
await Service.count().then(function (newcount) {
expect(newcount).toBe(count + 1);
});
})
.catch(err => console.log(`Error ${err}`));
});
});
For me the test looks fine, but when I run it I get:
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
Is something missing or is there even a better way to test a POST request? with Jest?
It is because you are not calling the done callback passed in jest callback function. It can be done like this.
test('Create a valid Service', async(done) => {
const service = {
name: "cool",
description: "description"
};
await Service.count().then(async function (count) {
await request(app)
.post('/api/services')
.send(service)
.then(async() => {
await Service.count().then(function (newcount) {
expect(newcount).toBe(count + 1);
// execute done callback here
done();
});
})
.catch(err => {
// write test for failure here
console.log(`Error ${err}`)
done()
});
});
});
You can also write this code in this way so that the readability can be improved and maximize the use of async/await.
test('Create a valid Service', async(done) => {
const service = {
name: "cool",
description: "description"
};
try {
const count = await Service.count();
await request(app).post('/api/services').send(service)
const newCount = await Service.count()
expect(newCount).toBe(count + 1);
done()
} catch (err) {
// write test for failure here
console.log(`Error ${err}`)
done()
}
});
By default Jest also resolves the promise in async/await case. We can achieve this without the callback function also
test('Create a valid Service', async() => {
const service = {
name: "cool",
description: "description"
};
try {
const count = await Service.count();
await request(app).post('/api/services').send(service)
const newCount = await Service.count()
expect(newCount).toBe(count + 1);
} catch (err) {
// write test for failure here
console.log(`Error ${err}`)
}
});

puppeteer evaluate results in context destroyed

Trying to wait for DOM mutations to stop but ends with Execution context was destroyed., any suggestion is welcome
page.evaluate(() => {
return new Promise((resolve,reject) => {
var timerId;
function resetTimer() {
clearInterval(timerId);
timerId = setTimeout(() => {
resolve(true);
}, 3000)
}
new MutationObserver(
() => {
resetTimer();
}
).observe(document.getElementById('root'), {
attributes: true, childList: true
});
resetTimer();
})
})
})
Protocol error (Runtime.callFunctionOn): Execution context was destroyed. undefined
at Promise (node_modules/puppeteer/lib/Connection.js:198:56)
at CDPSession.send (node_modules/puppeteer/lib/Connection.js:197:12)
at ExecutionContext.evaluateHandle (node_modules/puppeteer/lib/ExecutionContext.js:71:75)
at ExecutionContext.evaluate (node_modules/puppeteer/lib/ExecutionContext.js:46:31)
at Frame.evaluate (node_modules/puppeteer/lib/FrameManager.js:299:20)
The above snippet was run before getting a navigating lock on a page. Running the page.evaluate between navigation can throw this error.
Found this from,
Error: Protocol error (Runtime.callFunctionOn): Execution context was destroyed.
No page navigation lock ?
Fix was (at least in my case) to wait till URL changed and then page.evaluate.
Notes:
Are you using async/await in your code? If you have a promise but don't use any chain or async/await, then it should throw up errors like that.
Also you are firing the resetTimer out of observer callback too.
If not, then you are probably not monitoring the correct changes of that dom element.
Here is a a simple react app which changes state after 2 seconds.
class App extends Component {
constructor() {
super();
this.state = { bar: "foo" };
}
componentDidMount() {
setTimeout(() => this.setState({ bar: "not foo" }), 2000);
}
render() {
return <div className="App">{this.state.bar}</div>;
}
}
Here is the modified code for the above snippet.
await page.evaluate(() => { // wait for it to resolve
return new Promise((resolve, reject) => {
var timerId;
function resetTimer() {
clearInterval(timerId);
timerId = setTimeout(() => {
resolve(true);
}, 5000); // resolves after some time
}
const observer = new MutationObserver((mutations) => {
// show me the changes
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
// reset timer etc.
resetTimer();
});
// observe a lot of changes
observer.observe(document.getElementById("root"), {
attributes: true,
characterData: true,
childList: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true
});
});
});
Here is the result:

node-celery asychronise, await client.call() finish it's inner function

I am using node-celery 0.2.8 in my node.js server,and I create, use await on client.on(), hoping the code will console log result first, then log 'finally'. But the code console log 'finally' first, then after go through all the code, console log result. Could anyone help me to let the await work on client.on()? I just hope the code can execute in order, and wait at the asychronise request.
async(ctx) => {
const client = celery.createClient({
CELERY_BROKER_URL: 'amqp://guest:guest#172.**.2.**:5672//',
CELERY_RESULT_BACKEND: 'redis://172.**.2.**:6379/2',
CELERY_ROUTES: {
'street.add_account_multi': {'queue': 'street_default'},
}
});
await client.on('connect', () => {
client.call('street.add_account_multi', [crawlers], function (result) {
console.log(result);
});
});
console.log('finally');
}
Please try to put your operation after 'connect' event into a promise and then await resolve.
let clientConnect = new Promise((resolve, reject) => {
client.on('connect',() => {
client.call('street.add_account_multi', [crawlers], function(result) {
console.log(result);
resolve(result);
});
})
});
await clientConnect;
console.log('finally');

Resources