8th week of 2021

Highlights of the week.

Table of contents

πŸ‘¨β€πŸ’Ό Work

Episerver blocks + AutoMapper = 😿

I have previously written about using AutoMapper in an Episerver project to map models to view models. It's been quite nice so far.

This week I started creating mappings for blocks (reusable pieces of content). Whereas page models implement Episerver's IContent interface "normally," block models implement the IContent interface only during runtime. Not so nice.

This means that creating a mapping from IContent to a block type is not possible. At least not possible in this way that I tried:

public interface IContentViewModel
{
string ContentLink { get; set; }
string ContentType { get; set; }
// etc.
}

public class IContentProfile : Profile
{
public IContentProfile()
{
CreateMap<IContent, IContentViewModel>();
}
}

public class MyPageViewModel : IContentViewModel
{
// ...
}

public class MyPageProfile : Profile
{
public MyPageProfile()
{
CreateMap<MyPage, MyPageViewModel>()

// `MyPage` implements `IContent`, so this is ok:
.IncludeBase<IContent, IContentViewModel>();
}
}

public class MyBlockViewModel : IContentViewModel
{
// ...
}

public class MyBlockProfile : Profile
{
public MyBlockProfile()
{
CreateMap<MyBlock, MyBlockViewModel>()

// Error! `MyBlock` doesn't implement `IContent` until runtime,
// so this doesn't work:
.IncludeBase<IContent, IContentViewModel>();
}
}

I had to come up with a more convoluted solution. It's not ideal, but in the end it seems to work okay. I'll write a blog post about the solution after I have used it more extensively and confirmed that it's good.

πŸ‘¨β€πŸš€ Personal projects

Reached 5k points on Stack Overflow πŸŽ‰

After writing my 134th Stack Overflow answer, my total points reached 5,000. πŸ₯³ That's a nice milestone and a nice round number (which currently has grown to 5,020, so not so round anymore️).

I think this is good enough. I'll probably still post answers and questions when I have something to contribute, but I'm not going to actively aim for higher points. (And I normally haven't aimed for higher points, except during occasional bursts of Stack Overflow addiction.)

From time to time, I read about other people's negative experiences with Stack Overflow. The most recent such article was Done Answering Questions Stack Overflow by Gregg Tavares. Sounds sad; I don't want to experience anything like that.

I believe it's better to spend the effort on creating content for my own website. This way I'll own the content myself, my personal brand will grow stronger, I can avoid all the Stack Overflow drama, and I don't have to experience any more addiction. Plus there'll be less competition as the only person I have to surpass is me!

πŸ‘¨β€πŸŽ“ Learnings

React Testing Library: naming test cases from user perspective

At work, a colleague from another team gave a 1-hour presentation on front-end unit testing. He had named their test cases from user perspective, like this:

describe('<FooComponent>', () => {
test('user can see all options', () => {
// ...
})

test('user can toggle between options', () => {
// ...
})

// etc.
})

I was already familiar with the primary guiding principle of React Testing Library:

The more your tests resemble the way your software is used, the more confidence they can give you.

But I hadn't thought of naming test cases from this perspective. Makes sense, though! Naming test cases from user perspective helps you create good tests – tests that resemble the way your users use your software.

React Testing Library: using semantic queries

This is another thing that I learned from the presentation (see the previous section).

Your app probably has lots of text in various kinds of HTML elements: headings (<h1> – <h6>), paragraphs (<p>), form labels (<label>) and so on. What is the semantic meaning of each element?

Does your app have, or should it have, meaningful text in elements without a semantic meaning? Apart from plain paragraphs, probably not!

Testing Library's methods getByText(), queryByText() and findByText() search for all elements, i.e. they don't care about the elements' semantic meanings. Because most text should be in elements with specific semantic meanings, these methods should be used sparingly.

For example, don't look for a button using the getByText() method because the test could pass even if the button was implemented in a hacky way. (Think of a <div> with a click handler. Don't do it, kids – just use a <button>.) Use the getByRole() method instead:

getByRole('button', { name: /submit/i })

This ensures that the button you are looking for is implemented properly and in an accessible way.

So: prefer more specific queries than the basic getByText() method. See list of recommended prioritizations of the different queries.

πŸ•΅οΈβ€β™‚οΈ Cool stuff

Preventing npm install for non-supported Node.js versions

Stefan Judis has written a guide about how to prevent npm install for non-supported Node.js versions. It's easy – TL;DR:

  1. Add an engines property to package.json. E.g.:

    {
    "engines": {
    "node": ">=14.0.0"
    }
    }
  2. Add engine-strict=true to .npmrc. (Create the file if it doesn't exist.)

Stefan's guide has more info, so check it out.

I can immediately see how this would be useful in our team's project at work: at least two fellows have previously had obscure problems setting the project up because they had ancient Node.js versions. Node.js 8, anyone?!

πŸ’β€β™‚οΈ More Weekly log entries

Next week: 9th week of 2021

Previous week: 7th week of 2021