Creating a screencast from the commandline

A picture is worth a thousand words, that's why I always try to add screencasts when describing an issue I'm facing. I found it useful to be able to record my screen when I'm filing a GitHub issue about some UI or UX issue.

Screencast of a screencast. Wow, so meta

I have a method called gif-record in my command line toolbox that let me do that. It let me draw a rectangle on screen, record what is happening inside, and get a .gif as output to share as I please;

It seems pretty simple when explained like that, but is actually some kind of Frankeinstein's monster plugging command line tools together to get to the end result. In this article, I'll guide you through the pieces so you can build your own for your own needs.

First of all, I'm using slop with slop -f "%x %y %w %h" to draw a rectangle on screen, and get back the x,y coordinates, width and height. I then pass those coordinates to ffmpeg -f x11grab using the -s {width}x{height} and -i :0.0+{x},{y} options to tell it to record the screen at those coordinates.

ffmpeg comes with a lot of option flags, but what I'm using is -y to overwrite any existing file, -r 25 for a recording at 25FPS and -q 1 to get the best video quality possible.

To stop the ffmpeg recording, you can either Ctrl-C the command you started, or kill it by its pid. In my script (see link at the end of the article) I chose the second option, but won't get into more details about that here.

For the next step, I also use ffmpeg, but now that I have a video file, I'll convert it into a serie of still frames in .png format. The command I'm using is ffmpeg -i {input_video_file.mkv} -r 10 'frames/%04d.png'.

The -i marks the input file and the frames/%04d.png will define the pattern of the output files (in that case, they will be saved in the ./frames folder, with incrementing 4-digits names).

The -r flag is used once again to define the FPS. 10 is enough for my needs, as I record terminal output. It's smooth enough while keeping the filesize small, but feel free to increase it. I decided to keep my recording at 25FPS to have the smoothest recording possible, but adjust the still frame FPS depending on how smooth I want the end result.

Once I have all my still frames, I'll combine them into one .gif file. At this point, I would recommend removing some of the first and last frames as I always end up recording some garbage at the start and end. Determining the number of files I need to delete is easy to calculate based on the FPS I defined; if I want to remove 2 seconds at the start with and FPS of 10, it means removing the 20 first frames.

Converting png files into an animated gif can be done using convert (it's included in ImageMagick). The basic syntax is convert ./frames/*.png output.gif, but I also add the -delay 10 option to the mix. The actual value to pass to -delay will require some basic math: it should be equal to 100 divided by the FPS you defined earlier. For my previous example of an FPS of 10, the delay is 10, but if you had chosen an FPS of 25, the delay should then be set to 4 (100/25 = 4)

By default the generated gif will play once and then stop. I can control the number of times it loops by using the -loop option. A value of 0 will make if loop indefinitely (my favorite).

At this stage I thought I was done, but the generated gif will most certainly be too heavy to upload to GitHub issues as it's not optimized at all.

Compressing a gif will require a tool called gifsicle. But I should not use the official one but its giflossy fork. The original gifsicle does not have an option to compress files in a lossy format while giflossy (as the name suggests) can. Why are there two versions of the same tool in diverging branches? Well, OSS is hard.

Anyway, once the gifsicle fork is installed, I can used it with gifsicle input.gif --lossy=80 -o output.gif. The lower the value I pass to --lossy the more aggressive the compression will be. I also add --colors 256 to force the conversion of into a 256 palette.

And that's it. By plugging all those tools together, I now have a way to record parts of my screen and share the outputy, directly from my terminal.

You can have a look at my full implementation, wrapped in a ruby script if you're interested. You should also have a look at gifify which is the tool that I was originally using for converting videos to gif files.

(Not)Hibernating Ubuntu 16.04 with full disk encryption

To maximize the usage of my new laptop battery, I wanted to have it hibernate when I close the lid. I could see I had an "Hibernate" option in the settings, but it always stayed always greyed out and I could not select it.

One morning I sat at the table decided to fix this issue. First thing I tried was running sudo pm-hibernate to see if I could actually hibernate. The command did nothing except returning and error code 1.

After some Googling, I understood it had to do with my BIOS configuration. I have Secure Boot enabled in my BIOS, which seems to prevent hibernating.

One reboot later, after having disabled this option from the BIOS, I ran sudo pm-hibernate again, and this time I had much better results. My screen turned off for 2 seconds, then back again for 2 more seconds, then the laptop went to sleep.

Great! I'm making progress. So I'm pressing the switch button to turn it back on, but instead of coming back to my session, it initiated a whole reboot, going through the Lenovo splash screen and Ubuntu cryptsetup prompt.

More Googling told me that I need to configure GRUB to define what swap partition it should attempt to resume from. When you hibernate, the whole RAM memory is flushed to swap. GRUB needs to know the UUID of the swap disk holding that info.

To get the UUID, I typed sudo blkid | grep swap to get a list of all swap devices. In my case, I had two of them:

/dev/mapper/ubuntu--vg-swap_1: UUID="b0d3688c-e44a-4972-b18a-43e79ca3777c" TYPE="swap"
/dev/mapper/cryptswap1: UUID="78df939a-d7a9-46dc-9082-d46415cd6e0a" TYPE="swap"

Ok, so which one is it? Because I'm using Ubuntu full disk encryption, one of those two disks is actually the encrypted swap disk and the other is the "live" decrypted swap. But which is which?

swapon -s told me the name of the active swap: /dev/dm-3. Ok, that's a good start. sudo dmsetup info /dev/dm-3 yield the final answer: it's cryptswap1.

So cryptswap1 is the active swap; it means it's the decrypted swap, so ubuntu--vg-swap_1 is the actual encrypted swap. It actually makes sense, as the vg in the name stands for "Volume Group", a term used in LVM terminology.

My issue with that setup is that I cannot tell GRUB to resume on the decrypted swap, because the UUID of this swap will be randomly assigned at each boot and more importantly it won't be decrypted yet when resuming.

But I cannot tell GRUB to boot on the encrypted swap disk either as it will be random garbage from its point of view.

I was stuck. Other solutions online suggest that I could flush my RAM to a disk that was not encrypted so I could resume from it, but that defeats the purpose of encrypting my disks if I dump everything that is in RAM to a readable disk.

After hours and hours of Googling and trying, I was about to give up. That's when I decided to ask one of my coworker that I know has a similar setup: Linux on a Lenovo laptop, using encrypted disks.

His answer was all I needed:

over the years I just made my mind over the fact that hibernate was broken and I never even try to see if it's fixed, I just consider it as broken forever

Ok.

That's not just me then. Hibernating and having an encrypted drive are mutually exclusive. Too bad, I'd rather keep the encrypted drive.

Mocking in Jest

I used to do my JavaScript testing using a combination of mocha, expect and sinon, but Jest packages all those features into one cohesive package.

Transitioning to Jest has been smooth. The part that took me a while to figure out is how to properly mock methods, and this is what I'm going to develop here.

Mocking direct methods

Imagine a dummy component with two methods, foo and bar, with the following implementation:

const component = {
  foo(input) {
    return {
      id: input,
      name: component.bar()
    }
  },
  bar() {
    const alpha = 'bar';
    const beta = 'baz';
    return `${alpha}-${beta}`;
  }
}
export default component;

Calling foo(42) will return { id: 42, name: 'bar-baz' }.

The bar method is straightforward to test as it does not have any dependencies. All we have to test is that the output is the one we are expecting. The code can be simplified but I'm making it overly complicated here on purpose.

The point is that when we test foo, we don't want to deal with the internals of bar. We should be able to change the internals of bar and have our foo tests still work. Actually, we could even completly change the return value of bar and still have our foo tests pass.

To achieve that decoupling, the trick is to mock the bar method to control its behavior during our test.

import component from './component.js';

describe('component', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should have the name set to the value of bar()', () => {
    // Given
    const input = 42;
    const expected = {
      id: input,
      name: 'my-mock-name'
    };

    // When
    jest
      .spyOn(component, 'bar')
      .mockReturnValue('my-mock-name');
    const actual = component.foo(input);

    // Then
    expect(actual).toEqual(expected);
  });
});

The first step is to call jest.spyOn(component, 'bar'). This will replace the component.bar method with a mock version. By default the mock version will behave like the original method. Spying on a method has other benefits (like being able to assert the number of times a method is called), but those won't be discussed in this article.

Once we've replaced the original method with our spy, we can now call .mockReturnValue('my-mock-name') on it that will change the original method so it now always return my-mock-name when called.

The last step is to call jest.restoreAllMocks() in the afterEach hook. afterEach is called after each test, and restoreAllMocks will restore all our spies to their original methods. If we don't do that, all our component.bar calls in all our tests will always return my-mock-name.

Mocking dependency methods

This first part was about mocking methods of one of our dependencies. But how do you mock sub-dependencies? Let's update our component so it now uses one of its own dependencies:

import dependency from 'dependency';
const component = {
  foo(input) {
    return {
      id: input,
      name: dependency.bar()
    }
  }
}
export default component;

There is no component.bar method anymore here as component is directly calling its dependency .bar method. To mock dependencies, we need a bit more plumbing.

import component from './component.js';
jest.mock('dependency'); // <-- Here

describe('component', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should have the name set to the value of bar()', () => {
    // Given
    const input = 42;
    const expected = {
      id: 42,
      name: 'my-mock-name'
    };

    // When
    const dependency = require('dependency'); // <-- Here
    jest
      .spyOn(dependency, 'bar') // <-- Here
      .mockReturnValue('my-mock-name');
    const actual = component.foo(input);

    // Then
    expect(actual).toEqual(expected);
  });
});

We've added jest.mock('dependency') to our test file. It will tell Jest to replace all require and import of dependency with a mock object. This means that whenever we will import dependency (either in component.js or in our tests), it will be replaced with a mock version.

Like we did in the previous example, we will hardcode the return value of the bar method in our test. This time, we first need to import it (const dependency = require('dependency'), so we can spy on it and mock its return value.

Conclusion

Hope that overview can help others. It took me some time to understand how mocking was working in Jest and I hope this will help other figure out all the pieces.

Tested with Jest v21.1.0

Sending data to an iframe with Vue.js

Communicating from a parent window to a child iframe is a known problem in JavaScript and has already been solved. How to do it in the context of a Vue.js application is slightly different but based on the same principles.

I was confronted with the matter yesterday and had to find an elegant way to send data (credentials) from the parent window to a child iframe from my Vue.js app. Here's how I did it.

Initial markup

Both the parent (main window) and child (iframe) need some specific markup for this to work:

<!-- App.vue template -->
iframe(
  src='./iframe.html,
  v-on:load="onLoadIframe",
  name="myIframe"
)
// App.vue script
function findIframeByName(name) {
  return _.find(window.frames, frame => frame.name === name);
}

export default {
  methods: {
    onLoadIframe(event) {
      const iframe = findIframeByName(event.currentTarget.name);
      iframe.doSomething({
        appID: "SECRET_APP_ID",
        apiKey: "SECRET_API_KEY"
      });
    },
  },
};
<!-- iframe.html -->
<body>
  <script type="text/javascript">
    window.doSomething = function(parentData) {
      console.info(parentData);
    }
  </script>
</body>

How does this work?

When you'll load App.vue, the template will be rendered and will create the iframe, loading iframe.html in turn.

Because we added a v-on:load event on the iframe, we'll be able to know when the iframe will be loaded. At that point, we can read the property .currentTarget of the fired event.

This will give us a reference to the iframe HTML element. We can't do much with the HTML element; we can't access the window object that is inside of it directly.

But what we can do is reading its name attribute (myIframe). Then, using the findIframeByName method, we will loop over all the window.frames elements until we find the one that matches the name.

window.frames contains reference to all the sub-windows, so we can now start calling any method defined in the global window namespace of the child iframe. In our case, the window.doSomething method.

tl;dr

The basic trick here is to wait for the iframe to load, then get hold of a reference to its inside window object, to then call any globally available method available in it.

Importing iframe with Webpack and Vue.js

I've spent hours on a webpack + Vue.js + iframe issue yesterday. As I don't want all those hours to be completly wasted, I'm going to document my issue and the final solution.

The problem

I'm working on a Vue.js application, using Webpack for building all the assets. One of the pages need to include an iframe, loading a stand-alone keen-explorer.html file.

My problem was that the keen-explorer.html was not included in the final build and resulted in a 404.

Context: Why do I need to do that?

I need to instanciate a Keen.io explorer dashboard from inside my app. The keen-explorer JavaScript module cannot be imported from a script, as far as I can tell. It needs to be loaded inside the global window object, along with its dependencies (Bootstrap and jQuery).

I tried different ways to include it in my final Webpack build, but the iframe was the best solution I could find as it will isolate all the external dependencies from the rest of my app. Anyway, back to the problem at hand.

Naive approach and first 404

I'm using vue-loader to parse the content of my .vue files. Here is my template (using pug):

iframe(src="html/keen-explorer.html")

If I run webpack, if finishes without any error, but when loading the application, I have a 404 as html/keen-explorer.html cannot be found. Webpack did not include it in the final build and considered the src as any other attribute with no particular meaning.

Making vue-loader require iframe sources

Turns out that vue-loader does not automatically import the iframe[src] values by default. You have to update your webpack config to define what it should parse:

module: {
  rules: [
    {
      test: /\.vue$/,
      loader: 'vue-loader',
      options: {
        transformToRequire: {
          iframe: 'src',
        },
      },
    },
  ],
}

Now, if I rerun webpack, I have an error because webpack can't find html/keen-explorer.html. We're making progress, at least it tries to import it.

Fixing the filepath issues

vue-loader seems to treat all assets in the template as relative to the final output, while here I'm trying to reference a file present in my src/html directory.

First thing to do was to define a resolve alias in webpack to tell it where to look for files that starts with html/ using the following config:

resolve: {
  alias: {
    html: path.resolve(__dirname, 'src', 'html'),
  },
},

Then, you should not forget to prepend your src value with a ~ in the template, to tell vue-loader that this should be resolved as an import.

iframe(src="~html/keen-explorer.html")

Rerunning webpack results in no more filepath errors. The file is found and included in the final build.

Problem is... the filepath has been replaced by the content of the file, not its path in the final build. That means that my iframe is now trying to load <doctype>[...], which does not work at all.

Replacing the file with its built filepath

We're approaching the end and the solution is near. The last thing missing at that point is a way to replace the imported file by its built filepath in the final output.

Thankfully, that's what the file-loader loader is for:

module: {
  rules: [
    {
      test: /\.html$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: '[hash].[ext]',
          },
        }
      ],
    },
  ],
},

Doing so I can have an iframe with a src set to the relative filepath to the built html file.

Conclusion

It took me about 25mn to write this article, but more than 6h to hunt and debug this issue from start to finish (not counting all the wrong leads trying to embed the iframe in base64).

To recap, if you want to load a standalone iframe from your Vue.js application suing webpack you need to:

  1. Configure vue-loader so if follows iframe[src] attributes
  2. Prepend ~ to your src values
  3. Configure the resolve alias so filepath can be resolved by webpack
  4. Add a file-loader to process .html pages