Automating tasks with that nightmare

Motivation

Recently I came across nightmare on Github and started to play around with it.

There are quite a few tedious tasks I long wanted to automate at work. They all involves interacting with a browser and nightmare seems to be a good fit. So I decided it’s time to write a few scripts.

We internally use a wiki system for a lot of things and the system is not exactly a delight to use. For example, we need to fill out a form to submit a test request on the wiki after a bug fix. There are a few fields we need to fill out. After filling out the bug number you just fixed, you also need to fill out a short description about that bug. This piece of information can be found on the original bug report page. So everybody basically just copies and pastes the short description from there.

It would be great if that field can be automatically filled out after I input my bug number. But sadly that old system doesn’t do that.

So I started scripting my way out of it.

Scripting with Node

I would’ve used Python and Selenium since I used that in the past. But I’m mostly using JavaScript and nodejs these days. So I decided to use nightmare instead. nightmare looks mature enough and it’s based on Electron!

So I started.

Making it Run

To directly run a node script you need to add #!/usr/bin/env node on the top of the script. Then use chmod u+x myscript to make it excuatable. I also created a symlink for the script so I can run it as a proper command.

I used yargs to parse commandline options.

Making it Run Stuff

So the goal for the script is very clearly defined. I just need to type something like submittest -b 12345 --submit and it will automatically:

  • upload my stuff to a ftp server
  • fill out a form with the bug number 12345, the bug description and the location of my stuff on ftp
  • submit the form
  • change the status of that bug on the bug report page

Halfway through writing my script, I realized that sometimes we submit one bug test for several related bugs. So I need to get the descriptions for all these bugs.

Oops. It had been going really well.

Now I foresaw a loop. And I was not excited. 🙄

Okay… What are the options?

Option 1: Forget the loop and just get the description of the first bug. That field is not important… The bug numbers are the important thing to fill out on that form.

Option 2: Let’s think of a way to get all descriptions.

I admit that many times in the past I’ve chosen the easier option in similar situations but this time I wanted to make it work perfectly.

Now what?

I could use aync await, right? I remember I loved it when I used it in React Native.

Nope, it turned out that requires a high version of node (7.6 to be exact) and I don’t want that. Other people just might use my script in the future and I don’t want them to upgrade their node just because of this.

After a bit of digging around, I decided to use a generator function and vo.1

The final script looks something like this,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// parse bug numbers from commandline
function* run() {
let bugDesc;
let descArray = [];
yield nightmare
.goto("a login page")
// fill out login form
.click('input[type="submit"]')
for (let buglink of buglinks) {
let text = yield nightmare.goto(buglink).wait(300)
.evaluate(function() {
return document.querySelector('an awful awful selector').innerText;
});
descArray.push(text);
}
// Yay now we have it!
bugDesc = descArray.join('; ')
// finally fill in the test request form on another page
yield nightmare.end();
}
vo(run)(function(err, result) {
if (err) {
throw err;
}
});

That selector to get the description of the bug is awful. There is no class or id associated with it so I had to select the n-th tr and the m-th td to get that text. 🙃

It worked perfectly, yayyy! 🎉🎉🎉

On the other hand, controll flow in node still weirds me out.


  1. 1.As I was writing this post, I found this helpful post on this exact issue.