Updating Extwee with mocking console warnings using Jest

While I thought my work on Extwee would temporarily end with its newer 2.2.0 update, I’ve continued to work on the code and refine things based on some issues raised around edge cases using special characters in Twee output from Twine. This has meant returning to how I was testing different input and, accidentally, learning more about console testing in the framework I use named Jest.

Language Knowledge Doesn’t Always Translate

I wish I could write my knowledge of advanced testing patterns was better, but much of my programming-related learning over the last two years has been caused by a need to better understand game design and development patterns in order to teach them. Usually, but not always, this has meant learning patterns and structures related to C# for Unity or Blueprints for Unreal. When it comes to JavaScript, I don’t always stay as up-to-date on unit testing outside of the basics to teach React. This has meant my own programming in JavaScript is often simpler, and I do not always engage with the more advanced ways of doing things like mock testing patterns.

I have tried to address this lack of knowledge recently as part of a theme of “looser parsing, stricter compilation” I’ve been slowly implementing in a new Extwee minor update. Rather than throwing errors if a required attribute is missing from an incoming HTML element, for example, I’ve changed the code to generate warnings and assume a default value. When it comes to compilation and the production of an output format, however, the rules are strictly enforced. Anything that is flagged as required in a specification absolutely must be part of the output produced by Extwee.

Warnings Over Throwing Errors

Because of the adoption of this new theme in programming Extwee, I’ve had to move away from a pattern I was using frequently where I was testing for an error being thrown. For example, if you try to set the creator property on the Story object to a number, it will throw a TypeError with a supported test:

it('Should throw error if not String', () => {
expect(() => {
s.creator = 1;
}).toThrow();
});

Previously, if the name attribute was missing when parsing the <tw-storydata> Twine 2 HTML element, Extwee threw an error and stopped. In the new update, it instead produces a warning using the Console:

/**
* name: (string) Required.
* The name of the story.
*/
if (Object.prototype.hasOwnProperty.call(storyData.attributes, 'name')) {
// Set the story name
story.name = storyData.attributes.name;
} else {
// Name is a required field. Warn user.
console.warn('Warning: The name attribute is missing from tw-storydata!');
}

This shift caused me some trouble initially. I knew how to test for an error, but testing for a warning was not something I had previous experience using in Jest. After investing some examples and reading the documentation on “spying” on methods, I discovered it was possible to mock the Console by spying on an object and then checking if a method was called with a particular argument.

In the new testing block, there is some setup and takedown needed, but testing for calls to console.warn() can be captured and tested for safely.

 describe('Warnings', () => {
beforeEach(() => {
// Mock console.warn.
jest.spyOn(console, 'warn').mockImplementation();
});

afterEach(() => {
// Restore all mocks.
jest.restoreAllMocks();
});

it('Should generate a warning if name attribute is missing from tw-storydata', () => {
const s = '<tw-storydata ifid=\'E70FC479-01D9-4E44-AC6A-AFF9F5E1C475\'></tw-storydata>';
parseTwine2HTML(s);
expect(console.warn).toHaveBeenCalledWith('Warning: The name attribute is missing from tw-storydata!');
});
});

This new change of using the mock functions in Jest has helped tremendously in keeping track when console.warn() is called and when, previously, it would have been an error or other issue. With the changes I’m now working on in Extwee, everything is captured and any possible output, including errors and console messages, are tested against along with clear labelling on what is and is not an error as reflected on the command-line.