Storing The Last Dispatched Action In Aurelia Store

Another day, another Aurelia Store tip/hack. Recently, I encountered an interesting situation where I wanted to know when a specific action has fired, being able to check for it within my state subscriptions.

Now, it is highly likely there is a better way to do this. RxJS is quite magical and has a seemingly bottomless trove of treasures and ways to work with observables and data.

The use case is simple. Inside of my subscriptions, I want to know if a specific action has fired and in some cases, what the parameters for said action were. I decided this was the perfect use case for an Aurelia Store middleware.

function lastCalledActionMiddleware(state: State, originalState: State, settings = {}, action: CallingAction) {
    state.$action = {
        name: action.name,
        params: action.params ?? {}
    };

    return state;
}

This is the middleware function and registration. It sets a property defined in your state called $action which stores the currently fired action passing through the middleware as well as the parameters supplied. I prefix with a $ to make the chances of it being overwritten elsewhere highly unlikely.

When registering the middleware, I only want to know when it has fired. So, I choose to place it after.

this.store.registerMiddleware(lastCalledActionMiddleware, MiddlewarePlacement.After);

As you can see below in Redux Developer Tools, my property is being stored and the parameters supplied to the dispatched action.

If you know of a better way to do this, I would love to hear about it. For my use cases, this works quite well and fine. A middleware seems like the perfect use case for something like this.

Some Small Blog Changes Preempting Aurelia 2

As Aurelia 2 draws near, I have made some changes to my blog. Acknowledging that many of you rely on my blog as a source of information and help with Aurelia 1, I have created an Aurelia 2 category and renamed the existing category to Aurelia 1.

This will hopefully alleviate any confusion that you might have when Aurelia 2 is released. While many of my Aurelia 1 articles will still be relevant, they might not always apply to the new version of Aurelia.

Until Aurelia 2 is released, these changes do not currently mean much. But, after release, they will.

Another change you might have noticed is a new theme. The existing theme served me well for years, but now, it is time to try something newer and still easy to read. I am using the Less theme.

Getting Typescript 3.7 To Work With Webpack and ts-loader

At the time of writing this post, TypeScript 3.7 is in beta. Eventually, this post will become irrelevant. But, for the moment if you are trying to get TypeScript 3.7 Beta or any of the RC builds working with Webpack and ts-loader you might have encountered a bunch of red text in your console.

In my case, I had target: "esnext" set in my tsconfig.json file which the ts-loader plugin should read and set the appropriate settings. And yet, TypeScript 3.7 Beta was not working despite making sure everything was up to date.

It turns out at present, ts-loader does not seem to work with esnext as the target value (hopefully, this changes when TypeScript 3.7 is released). To get things working, all you need to do is change your target value in tsconfig.json to es2018 like this: "target": "es2018"

In my case, that fixed the issue and I could use the exciting new features TypeScript has to offer such as Nullish Coalescing and Optional Chaining. Happy days.

Getting a 404 Error Dealing With File Uploads To Storage In Firebase?

I am a huge proponent of Firebase and interestingly, up until recently, I had never used Firebase Storage. Instead, I usually opt for Amazon’s S3 service which I am familiar with. Wanting to keep everything in the one service is appealing to me, so I started to add in file upload functionality in a Firebase Cloud Function.

It was not as straightforward as I would have hoped. I am using Express with Firebase and the Google Cloud NPM package as documented in code examples and numerous tutorials.

After adding in the @google-cloud/storage package into my Cloud Function file, I plumbed it all in and figured it would work. Then I got this vague error.

{
    "errors": [
        "domain": "global",
        "reason": "notFound",
        "message": "Not Found"
    ]
}

Even with an error message, I was left scratching my head. I had nothing I could really Google, looking through the documentation failed to give me the answer and then out of frustration and trial and error, I discovered the cause.

This is the code that I had setting up my storage object and bucket instance. Tell me if you can spot the problem.

import { Storage } from '@google-cloud/storage';
const storage = new Storage();

const bucket = storage.bucket('steem-engine-dex');

The problem is with the bucket name, I also had to add in the project ID as well.

storage.bucket('steem-engine-dex.appspot.com', {
    userProject: 'steem-engine-dex'
});

The bucket name in Firebase requires adding .appspot.com into the bucket name and sure enough, if you go into Firebase itself and into the Storage section, you’ll notice something that points to this.

The bucket name after the protocol gs:// actually has the bucket name with appspot.com in it. Unless I am blind, nowhere does it mention any of this in the documentation, it really tripped me up.

I wasted a lot of time working this out. So, hopefully, if you have experienced the same issue, this blog post saved you a few hours or days worth of wasted work. Happy coding.

Jest Not Finding Tests In Travis CI? You Might Be Ignoring The Build Folder

Recently, I encountered an issue in Travis CI and my Aurelia application which uses Jest for the tests suite. I erroneously copied some older configuration code for Jest and used it in my Jest configuration, assuming it would work.

This error took a while to isolate and resolve. It was out of pure desperate that I accidentally discovered the fix. If I made the mistake, it is possible that you might as well. Does this sound familiar to you?

Tests were working locally. Running Jest would run my tests and they would all pass, giving a nice 0 code, happy days. Pushing my changes up causing my Travis CI build to automatically run would result in an error. The error looked like the following (this is the actual error):

One thing you will notice is my testRegex is saying there are 3 matches, which is correct. I have three test files which run and work locally without issue. I tried modifying my testRegex I tried changing the location of my tests, nothing worked. Then I noticed I had testPathIgnorePatterns defined in my Jest configuration (part of my older config).

This is my actual code. I was ignoring my build, dist and sample directories from testing. It turns out defining this locally causes no problem, but causes Travis CI to fail. At first, I was confused, why is this breaking Travis CI.

Then it dawned on me. It’s the fact I am using some outdated configuration option, the issue is the path in which tests and code are run inside of the virtualised Travis CI environment.

Can you spot the issue? The path is /home/travis/build/steem-engine-exchange/steem-engine-dex what is contained within this path? A folder named buildwhich is located under /home/travis ignoring build causes this to get matched and thus, it breaks the tests from running.

My innocent ignore rule was being triggered only in Travis CI, not locally because locally my files are run from my home directory and not within a build directory.

Using Wallaby.js With Aurelia, Jest and TypeScript

Wallaby.js is one of the most amazing additions you can make to your testing workflow. I have been a happily paid user for a couple of years now and if you are looking to up your testing game, I highly recommend it.

Chances are if you are reading this post, you already use Wallaby and you are looking to get it working in your Aurelia applications with Jest and TypeScript. It’s a combination that is not all too uncommon these days, TypeScript is the future.

The Wallaby.js configuration itself requires very little code to work out-of-the-box with Aurelia and Jest.

module.exports = function (wallaby) {

  return {
    files: [
      '!**/*.css',
      'src/**/*.ts',
      'src/**/*.html',
      'test/unit/helpers.ts',
      'test/unit/mock-data/**/*.ts',
      'test/unit/stubs/**/*.ts',
      'aurelia_project/environments/**/*.ts',
      'test/jest.setup.ts',
      'tsconfig.json'
    ],

    tests: [
      'test/unit/**/*.spec.ts'
    ],

    compilers: {
      '**/*.ts': wallaby.compilers.typeScript({ module: 'commonjs' })
    },

    env: {
      runner: 'node', 
      type: 'node'
    },

    testFramework: 'jest',

    debug: true
  };
};

The above configuration assumes your files live in src and in my case, inside of my test/unit directory I have a helpers.ts file which has some functions making testing easier, a mock-data directory for mock data to import, a stubs directory for stubbing out certain mocks as well as my main Jest configuration file jest.setup.ts.

Do not copy the above file line-for-line. Make sure it reflects your project and the files inside of it.

In the compilers section we have to tell Wallaby to compile our TypeScript to commonjs format. In my case, I was originally targeting esnext as my module format and Wallaby does not work with modern JS syntax just yet. the rest is fairly explanatory.

Here is what Wallaby looks like running in an application I am currently working on.

Implementing A Method To Get Single Values In Aurelia Store

The Aurelia Store plugin is a great way to add powerful and easy to use state management into your Aurelia applications. For some, the paradigm shift in how you work can be confusing to work with at first. One such task you might find yourself needing to do is obtaining a singular value from the state.

As you will soon discover, state management in Aurelia Store requires you to subscribe to the entire state and react to all changes. Sometimes you just want to get a single value from the state to reference inside of your view-models.

While I cannot guarantee Vildan (Aurelia core team member and author of Aurelia Store) will approve of my solution, it works 😂

The solution I came up with which fits my needs perfectly is a method to subscribe, get one or more values from the state and then unsubscribe.

export const getStatePropertyOnce = async (...properties: string[]) => {
  return new Promise(async (resolve, reject) => {
    const subscription = store.state.pipe(pluck(...properties)).subscribe(
      (value) => { resolve(value); },
      (error) => { reject(error); }
    );
    subscription.unsubscribe();
  });
};

The beautiful thing about this method is it not only supports top-level properties, but it also works for nested properties in your state object as well. In a real use case, I have the locality value of the user’s client stored in the state.

async getAppClient() {
    this.appClient = await getStatePropertyOnce('appClient');
} 

There might be some pitfalls I have yet to encounter with the above functionality and you should also make sure your await is wrapped in a try/catch block as well to handle errors, but it works for my needs. If the thought of having to use @connectTo or manually setup a subscriber and then dispose of it within your view-models sounds like a lot of work, this helper method can be a good time saver.

async getValueFromStore() {
    // Object in state is represented as user.details.email
    this.someValue = await getStatePropertyOnce('user', 'details', 'email');
} 

In the above example, we are using the method to get a property of email which exists on an object called details which lives inside of a main user object. The method handles getting nested properties with ease.

Ryan’s Toy Review Is Child Exploitation

Being a parent, as any other parent would attest is rewarding, but hard work. And nothing has made modern parenting more challenging than YouTube.

We managed to not expose our firstborn son to any TV or screens until he was two. We were doing well until we had a trip booked from Australia to the UK, which was a 23-hour trip. We bit the bullet and bought an iPad to load up with some activities for the journey and some carefully selected YouTube videos.

Things started innocently enough, until the YouTube app, when connected to the internet, would begin automatically playing similar recommended videos. I do not recall the exact moment, but eventually, Ryan’s Toy Review found its way into our lives.

If you are a parent of children who watch YouTube, there is a good chance they have come across Ryan’s Toy Review or one of its other associated channels like Ryan’s Family Review.

Ryan’s Toy Review might have started out with good intentions, but estimates place the earnings of the channel at around $22 million (US Dollars) per year. When you are earning that much money, you are a business, not an innocent child playing with toys.

The parents regularly feature in Ryan’s videos, the mother has a very irritating voice and is always shouting. The father seems to be softly spoken and somewhat likeable, but the mother takes centre stage, and it sometimes feels like she is the subject of the video, not Ryan.

Watch any of the videos produced lately on the channel, and you will notice they all have one thing most in common: they’re advertising Ryan branded products. None of the videos has any kind of disclaimer on them that says they are promoting paid products either. Sometimes a flash of text or a few seconds of the video will tell you it’s a promotion, but intentionally short.

In one of the videos, Ryan visits a Colgate factory and does a tour, the video starts off innocently enough until you realise it’s an advertisement for Ryan branded dental products. The way they frame it is Ryan just spends a fun day making toothpaste and mouthwash, like it’s a visit to the museum.

The ironic thing about these videos, is there are numerous ones of Ryan eating and playing with chocolate, talking about McDonald’s and other unhealthy forms of food that would cause decay.

People are beginning to notice that Ryan’s Toy Review and associated channels are deceiving and tricking children into buying their products. So much so, a complaint has been registered with the FTC.

And it is clear the parents are reaping the rewards of the success of the channel. They started another channel called Ryan’s Family Review, where they travel and do things most kids do not get to do. Perhaps most desperate of all is the channel his parents started called The Studio Space.

We blocked Ryan’s Toy Review and Family Review channels, but one channel which is new is Studio Space (since blocked). Most of the videos centre around Ryan’s parents and other weird guests, with a rare appearance from Ryan himself from time-to-time.

I get the impression Ryan’s mother Loann, loves fame and money because she appears the most in these videos. It feels like a desperate attempt to pivot, knowing that Ryan is getting older and becoming more interested in video games like Minecraft over toys.

The Studio Space videos see Ryan’s parents acting out role plays, playing with toys, pushing cardboard boxes into inflatable pools and other nonsense in a desperate attempt to retain their large following and subsequent profits from that following.

In the regular YouTube app, you cannot outright block a channel from being watched or appearing. However, if you download the YouTube Kids app, you can. We actually have blocked the channel in our house, and I recommend everyone else does the same.

Ryan’s Toy Review is a business selling products to vulnerable children disguised as innocent toy review videos.

Mocking Default Imports In Jest With TypeScript

If you are writing tests using Jest and you use TypeScript, there is a good chance you have encountered an error along the lines of TypeError: defaultsDeep_1.default is not a function or TypeError: myClass.default is not a constructor when trying to test a file that is using a default import from a module.

You most likely have read countless StackOverflow questions, but none of the solutions will solve the issue. You’ve read the Jest documentation (which is quite extensive), but still no mention of mocking default module imports with TypeScript.

In my case, I had this error when trying to import a Lodash function defaultsDeep and another when importing the Input Mask module. My imports look like the following.

import defaultsDeep from 'lodash/defaultsDeep';
import Inputmask, { Options, Instance } from 'inputmask';

Inside of my test which will be testing this specific file, I use jest.mock to mock the specific modules and their implementations. The important thing to note here is I am returning default from within my mocks. This is because of how default imports are transpiled within TypeScript.

The Lodash mock is more simplistic:

jest.mock('lodash/defaultsDeep', () => {
  return {
    default: jest.fn()
  };

In the case of Input Mask, I needed to mock an instance which has a method on it. The usage in the actual file highlights what we want to achieve. The input mask plugin is newable, it then exposes a mask method which we supply with an element.

this.im = new Inputmask(options);
this.im.mask(element);

This is how we mock the above module and accommodate for the usage:

jest.mock('inputmask', () => {
  return {
    default: jest.fn().mockImplementation(() => {
      return {
        mask: jest.fn()
      };
    })
  };
});

The convenient thing about the solutions presented is they will work for all default imported modules. Have fun.

My Experiences One Year (and counting) Working From Home

A little over a year ago I took a new job and because the office is close to an hour and a half away, I wanted to work remotely for most of the week. Commuting upwards of three hours a day five times a week would have destroyed me.

So, while I don’t work 100% of the week remotely, I work two days in the office and three days at home. Everyone has their own experiences working remotely, and I thought it would be interesting to share my perspective and experience.

Oh, and for context, I have a four-year-old son and a six-month-old daughter. My son goes to daycare three days per week, and there is some overlap with the days I work from home, my wife is a stay at home mother and also currently studying as well.

You need a dedicated space

You need a consistent working space. Working from home if you love what you do, it’s not hard to be disciplined, if anything, it is difficult to stop working once you start (more on that later). I often hear people remark when I tell them I work from home they would find it hard not to watch Netflix or slack off.

Working remotely while more relaxed, you should still act like you’re in an office. Get yourself set up with an office or corner of a quiet room, buy a comfortable chair and a sturdy desk.

Noise cancelling earphones are a must

If you work from home and you cannot guarantee that you will only ever be there alone in business hours, get yourself some noise-cancelling earphones. For me, it’s a crying baby and an energetic four-year-old running around the house, the TV going or music.

It’s not fair to expect everyone else to change their routine or how they go about their day for the sake of making it work office like for me at home. Also, if you live near a busy street, you’ll have outside noise like cars and motorbikes.

At present, I have to deal with a loud street sweeper making hourly trips up and down my street. This is because of some development work happening at the end of the road.

It can be harder to stop working

Some people have laughed when I tell them that working from home makes it harder for me to stop. With no commute to and from the office, I find I start earlier and finish later quite often because being in the comfort of your own home can be deceiving.

For me, this is really the only downside. Fortunately, my wife is great at making sure I finish at 5 pm a lot of the time, mainly because I help with the night time routine and I can help free her hands up to do other things not related to the kids.

Slack makes the distance more comfortable to manage, mostly

For some, the lack of office co-workers is a dealbreaker for them. And admittedly, at times not having a co-worker to bounce an idea off of or head out to lunch with can be a bit of a drag at times.

For those times, when I need to speak to someone, they’re only a Slack message or video call away. Because I live in Australia, and we have embarrassingly slow internet (my connection is decent compared to the average), sometimes there are technical difficulties getting on a video call because our main office can’t get a proper connection in the area yet (despite the fact it ironically is one of the first places in Australia to have 5G rolled out).

I highly recommend if you’re working remotely to install the Visual Studio Code LiveShare extension, it allows remote coding sessions (including the ability to share a terminal), so you can do remote pair programming and troubleshooting as well.

You save a lot of money

By cutting my commute time 80%, we are refuelling our car a lot less (once every 1.5 weeks). What felt like twice-yearly car services, has now become just the one annual service. We are putting fewer kilometres on the odometer, which is a considerable saving. Special shout out to the environment, I’m reducing my carbon footprint in the process.

And then you have the saved money on coffee and food. When I used to work in an office, I would eat out more often than I care to admit. This is usually how you bond and socialise with your colleagues, over a nice takeout lunch. My wife is the queen of food preparation, so we always have a good selection of lunches to pull out of the freezer for lunch.

Then you have the big wallet drainer, the big “C” coffee. I am not sure how much a cup of coffee costs where you are, but where I live it’s on average AUD $5, which is about USD $3.40. It adds up really quick when you can drink upwards of four coffees per day (especially during meetings).

At home, we invested in a coffee machine, and if you work remote and love coffee, I highly recommend splurging on a coffee machine and some quality beans of your choosing. The cost per cup is so low, the only dangerous thing you need to watch out for is drinking too much coffee.

The savings don’t stop there. Because I work from home, at tax time, I am allowed to claim some costs as work-related expenses. I can claim part of my internet bill, cost of buying stationery and other things your accountant can help you out with. Getting some money back from the government is always a good thing.

So, while I am probably spending more on AC during the summer months, using my electricity and water, it still works out cheaper than a commute to an office and eating out. Winning.

Increased productivity

Working from home makes you so much more productive if you can trust yourself to be disciplined and not sit on YouTube all day long. Working in a traditional office is full of interruptions, detrimental to productivity. There is nothing worse than getting in the zone and having someone tap you on the shoulder, or meetings that run overtime.

When someone wants to ask a question, they have to Slack me, and if I am set to busy aka do not disturb, I won’t even see the message until I check. I get to reply on my own terms.

You get sick less

If you have worked in open space offices (or any office), you will be familiar with office plagues. There is always that one or more persons who come into work sick (like it’s a badge of honour) and spread their sickness around like Germaclaus (an ill version of Santa that spreads sickness).

I have only been sick once this past year and like I said in the opening of this post, I have a son who goes to kindergarten three days per week. The fact I get sick less with a child in kindy than I do working in an office, it says a lot.

Last, but, not least…

I get to work in my pyjamas. On those three days, I am at home, I get into the shower and then put on some comfortable pants and a shirt. There is nothing like working in whatever you want to wear.

When I am in the office, I have to put on an ironed collared shirt and chino pants with a belt, but at home, I am one step away from being ready for bed.