Category: Uncategorized

  • Completing form fields with Puppeteer

    Recently we had some issues with flakey e2e tests, which turned out to be caused by the Puppeteer page.type method sometimes not inputting the full text into form fields. In one case this resulted in a user account getting created as contri instead of contributoruser, with the following test failing as it wasn’t able to log in with the correct account details.

    These failures were intermittent and seemed to be related to the speed at which Puppeteer was inputting the text. With the page.keyboard.type method, there is an option to pass in a delay config option, which seemed to improve things for some tests.

    With the user account creation form though, it seemed like typing each letter into input fields was not the most efficient or robust approach. We were not testing the user creation form in these tests, so didn’t need to know how it was performing with actual keyboard input.

    Is there a way to instead set the input field value in one go rather than letter by letter?

    It turns out there is. With the following approach, we were able to set each of the form inputs with a single string rather than one letter at a time. With this change, our most recent problem test now seems to be passing much more reliably, locally and in CI.

    await page.$eval( '#user_login', el => el.value = 'username' );

    This call was contained in a generic createUser method which allows the username to be passed in, and it turned out that to use a dynamic value it has to be passed in as a third parameter of the page.$eval method, eg.

    await page.$eval(
    	'#user_login',
    	( el, value ) => ( el.value = value ),
    	username
    );

    In the above example the username is the argument that gets passed into the value parameter.

  • Switching to Automattic

    After many years of commuting to a city office in a 5 speed manual I came across the opportunity to switch to Automattic, and ditch the commute altogether! I had been thinking about looking for a remote development role for some time, and while hunting for new opportunities at the end of 2018 I came across this advert for Javascript Engineers. I was tempted, but on looking at the work conditions it seemed too good to be true.

    I did however drift back to the vacancy page a couple more times for another read, and on the second or third visit the following message appeared:

    That was enough encouragement for me, what was there to lose, so on Christmas Eve 2018 I bit the bullet and submitted an application.

    Some time in January I received an invitation to attend an interview for the role. This initial interview was conducted entirely as a Slack chat. This made it the most relaxing job interview I have ever attended. It was amazing how much easier it made it to compose your thoughts and think of the best answers to the questions without the pressure of a group of people staring at you across the desk.

    The next step was a small coding challenge. Whew, no reversing a binary tree on a whiteboard here! This was a take home task of around 6 hours, with the opportunity of questions, code review, and discussion with a mentor, so no pressure to work in isolation and get it 100% correct the first time. There was still plenty of challenge in it to make it an interesting and satisfying task to complete.

    After a bit of a nervous wait to see if I had completed that challenge successfully a Slack message popped up asking if I would like to commence on the longer trial project on contract. As with the previous steps plenty of information was provided about what this would involve. While it is a reasonable time commitment, it is possible to stretch it out over up to six weeks to fit around your other commitments. I figured I had nothing to lose, and had made it this far, so went for it.

    Working remotely doesn’t mean being stuck all week in your spare bedroom – the view from one of my new co-working spaces

    The project was challenging, and interesting to work on, and the great thing was that a mentor was provided to answer questions, and provide code review and feedback along the way. This really did feel much more like a work project than a test. The code review comments were always helpful and friendly, and it was as much a learning opportunity as anything.

    Although this project did soak up quite a few hours, the interactions with Automattic staff, and being able to see some of the actual work discussions going on in other Slack channels, gave me a really good feel for what working at the company would really be like (you never really know what a company might be like to work at after the usual 1 hour work interviews).

    The effort paid off, and some time in April 2019 I was offered a role as a Javascript Engineer at Automattic, and started in the last week of May. The on-boarding process is incredibly organised. To say that the company is internationally distributed I have felt more immediately part of the wider team than in some previous roles where the whole team was in a single office.

    Two months into it, and it appears that those conditions outlined on the work for us page aren’t too good to be true. They really do let you take a paid two to three-month sabbatical every five years – two of my current team mates are off on theirs at the moment! Working from home may not be for everyone, but so far I am loving the freedom and flexibility it brings, and working with people from all over the world on a daily basis you certainly don’t feel lonely!

    If you have any questions about the hiring process, or working at Automattic, feel free to leave a comment.

  • RxJs File Chunking & Uploading service

    Recently I was presented with the challenge of writing some file upload components for a single page app. There are lots of existing upload libraries out there already, but due to an existing server side API that could not be changed I had trouble finding one that could do the job. The existing document processing service we were working against required the following:

    • The user had to be able to select as many files as they wanted for upload
    • The file had to be split into 1MB chunks
    • A CRC had to be calculated for each chunk and the overall file
    • Each chunk for a given file had to be uploaded sequentially, but multiple files could upload concurrently

    On top of this we wanted to limit the number of chunks that could be in progress at any one time in order to prevent the maximum number of concurrent connections to one domain being maxed out. We wanted the user to be able to continue to use the app while uploads were in progress, and this becomes impossible if all the http connections are tied up (less of a problem with http2, but still on issue on poorer connections).

    Given the async nature of all the steps in the process (chunking, checksumming, uploading) it seemed like RxJs would be an ideal solution. A little digging in the docs also revealed that the mergeMap operator even has a built in parameter to limit how many elements from the source stream should be processed concurrently. With this, the bulk of the work of queuing the chunks for upload is handled by 2 lines of code!

    chunkQueue$
        .pipe(mergeMap((data) => data, null, maxConnections))
        .subscribe();

    The other tricky part was working out how to provide a progress indicator, but luckily Axios provides a progress callback. It was reasonably straight forward to wrap the Axios promise based calls in an Observable in order to merge them in with the other RxJs streams, as below.

    export function httpUpload(file) {
        return Observable.create((observer) => {
            var config = {
                onUploadProgress: (progressEvent)  => {
                    var percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
                    observer.next({ progress: percentCompleted });
                }
            };
            axios.post(`${appConfig.apiUrl}/upload`, file, config)
                .then((response) => {
                    observer.next({ status: response.status });
                    observer.complete();
                })
                .catch((error) => {
                    observer.error(error);
                });
        })
    };

    A working example of the full chunking and uploading process  can be found at https://github.com/glendaviesnz/rxjs-file-chunker. At some point I may get around to adding some extra configuration and error handling in order to make it a more reusable module.

  • …. just moved here

    I used to occasionally ramble over there … but recently started working at Automattic, so thought I had better do some dog-fooding, so to speak, so now I will occasionally ramble here … all opinions are mostly my own, but sometimes appropriated.