<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>mfstapert.</title>
    <link>https://mfstapert.dev/blog</link>
    <atom:link href="https://mfstapert.dev/rss.xml" rel="self" type="application/rss+xml" />
    <description>Blog posts by Marijn Stapert</description>
    <language>en</language>
    <lastBuildDate>Tue, 20 Aug 2024 00:00:00 GMT</lastBuildDate>
    <item>
      <title>Testcontainers with github actions</title>
      <link>https://mfstapert.dev/blog/testcontainers-with-github-actions</link>
      <guid isPermaLink="true">https://mfstapert.dev/blog/testcontainers-with-github-actions</guid>
      <pubDate>Tue, 20 Aug 2024 00:00:00 GMT</pubDate>
      <description>I tried running testcontainers on github actions, the trick to running them was increasing the default timeout for your tests. Github has out of the box support for docker, there is a delay with startup though, so you need to account for that. I thought this was a hack, but the official testcontainers repo uses this approach as well.</description>
      <content:encoded><![CDATA[<p>I tried running testcontainers on github actions, the trick to running them was increasing the default timeout for your tests.
Github has out of the box support for docker, there is a delay with startup though, so you need to account for that.
I thought this was a hack, but the official testcontainers repo uses this approach as well.</p>
<pre><code class="language-json">{
  // jest config
  &quot;testEnvironment&quot;: &quot;node&quot;,
  &quot;preset&quot;: &quot;ts-jest&quot;,
  &quot;testTimeout&quot;: 60000
}
</code></pre>
<h2>About testcontainers</h2>
<p>In case you&#39;re wondering what testcontainers are; it&#39;s a library that provides lightweight containers for your tests.
It allows you to easily set up dependencies, so you can run your tests against in a &quot;real&quot; environment.
You can E2E test a CRUD backend with a real database, or test against an actual message queue.
It adds a lot of reliability since the dependencies are very close to the real deal, and it makes E2E/integration test set up a breeze.</p>
<p>Here&#39;s an example test using nestjs with drizzle as an ORM:</p>
<pre><code class="language-typescript">import { runMigrations } from &#39;@db/migrate&#39;;
import { schema } from &#39;@db/schema&#39;;
import { DrizzlePostgresModule } from &#39;@knaadh/nestjs-drizzle-postgres&#39;;
import { INestApplication } from &#39;@nestjs/common&#39;;
import { PostgreSqlContainer } from &#39;@testcontainers/postgresql&#39;;
import { StartedPostgreSqlContainer } from &#39;@testcontainers/postgresql/build/postgresql-container&#39;;
import request from &#39;supertest&#39;;

import { UserModule } from &#39;../src/user/user.module&#39;;

describe(&#39;users e2e&#39;, () =&gt; {
  let app: INestApplication;
  let postgresContainer: StartedPostgreSqlContainer;

  beforeAll(async () =&gt; {
    postgresContainer = await new PostgreSqlContainer().start();

    // add DATABASE_URL to env so we can use it for our migrations
    process.env.DATABASE_URL = postgresContainer.getConnectionUri();

    const moduleFixture = await Test.createTestingModule({
      imports: [
        DrizzlePostgresModule.registerAsync({
          tag: &#39;DB&#39;,
          useFactory() {
            return {
              postgres: {
                url: postgresContainer.getConnectionUri(),
              },
              config: { schema },
            };
          },
        }),
        UserModule,
      ],
    }).compile();

    // this also runs a seed method for testdata in non dev environments
    await runMigrations();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  afterAll(async () =&gt; {
    await app.close();
    await postgresContainer.stop();
  });

  it(&#39;Get users&#39;, () =&gt; {
    // Run actual http request against server with postgres database
    return request(app.getHttpServer())
      .get(&#39;/users&#39;)
      .expect(200)
      .expect([{ id: 1, name: &#39;Marijn&#39; }]);
  });
});
</code></pre>
]]></content:encoded>
    </item>
    <item>
      <title>Minimal ESLint and prettier setup</title>
      <link>https://mfstapert.dev/blog/eslint-prettier</link>
      <guid isPermaLink="true">https://mfstapert.dev/blog/eslint-prettier</guid>
      <pubDate>Tue, 14 May 2024 00:00:00 GMT</pubDate>
      <description>Recently I had the assignment to set up prettier and ESLint for new projects at my company. Most projects we do involve 2 webapps in a mono repo and 2-3 front-end developers working at a time. In this article I&apos;ll summarize my findings to save you, the reader, some time setting this up. A repo with this setup can be found at prettier-ESLint-angular-config on GitHub.</description>
      <content:encoded><![CDATA[<p>Recently I had the assignment to set up prettier and ESLint for new projects at my company.
Most projects we do involve 2 webapps in a mono repo and 2-3 front-end developers working at a time.
In this article I&#39;ll summarize my findings to save you, the reader, some time setting this up.
A repo with this setup can be found at <a href="https://github.com/MFStapert/prettier-ESLint-angular-config">prettier-ESLint-angular-config on GitHub</a>.</p>
<h2>.editorconfig</h2>
<p><code>.editorconfig</code> will be picked up by all editors unless specified otherwise.
This config has overlap with prettier, moving all config that is not unique to <code>.editorconfig</code>, to prettier config, makes both easier to maintain.</p>
<pre><code>root = true

[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
</code></pre>
<h2>prettier</h2>
<p>Prettier is as an opinionated formatter with little configuration options, using it without any configuration is perfectly justifiable.</p>
<pre><code class="language-json">{
  &quot;printWidth&quot;: 100,
  &quot;singleQuote&quot;: true,
  &quot;arrowParens&quot;: &quot;avoid&quot;,
  &quot;plugins&quot;: [&quot;prettier-plugin-organize-imports&quot;, &quot;prettier-plugin-organize-attributes&quot;]
}
</code></pre>
<p>Angular being pretty verbose justifies the increased print width for me, you can still view 2 files open on a small screen side by side.</p>
<p>For plugins; <code>prettier-plugin-organize-imports</code> is mandatory since it eliminates review comments about unused imports.
I would recommend <code>prettier-plugin-organize-attributes</code>, for consistency’s sake.</p>
<h2>ESLint</h2>
<p>ESLint can be an obtuse tool both in its usage and configuration.
I would not recommend using ESLint in combination with the angular CLI for the majority of projects.
Setting up ESLint with the angular CLI requires extra configuration and dependencies while adding no benefits in most scenarios.
I would consider the angular CLI in multi project repo&#39;s so projects can be linted individually.</p>
<p>For configuration, I use a single file, that can be extended:</p>
<pre><code class="language-json">{
  &quot;root&quot;: true,
  &quot;ignorePatterns&quot;: [
    &quot;**/build/**&quot;,
    &quot;**/coverage/**&quot;,
    &quot;**/dist/**&quot;,
    &quot;**/generated/**&quot;,
    &quot;**/node_modules/**&quot;
  ],
  &quot;extends&quot;: [&quot;prettier&quot;],
  &quot;overrides&quot;: [/* abreviated */]
}
</code></pre>
<p>Prettier configuration being extended here is the only exiting line since it&#39;s needed to play nice with prettier.
The overrides look as follows:</p>
<pre><code class="language-json">{
  &quot;overrides&quot;: [
    {
      &quot;files&quot;: [&quot;*.ts&quot;],
      &quot;extends&quot;: [
        &quot;eslint:recommended&quot;,
        &quot;plugin:@typescript-eslint/recommended&quot;,
        &quot;plugin:@angular-eslint/recommended&quot;,
        &quot;plugin:@angular-eslint/template/process-inline-templates&quot;
      ],
      &quot;rules&quot;: {
        &quot;no-console&quot;: [&quot;error&quot;, { &quot;allow&quot;: [&quot;warn&quot;, &quot;error&quot;] }],
        &quot;@angular-eslint/prefer-standalone&quot;: &quot;error&quot;,
        &quot;@angular-eslint/use-lifecycle-interface&quot;: &quot;error&quot;
      }
    },
    {
      &quot;files&quot;: [&quot;*.html&quot;],
      &quot;extends&quot;: [
        &quot;plugin:@angular-ESLint/template/recommended&quot;,
        &quot;plugin:@angular-ESLint/template/accessibility&quot;
      ],
      &quot;rules&quot;: {
        &quot;@angular-eslint/template/prefer-control-flow&quot;: &quot;error&quot;,
        &quot;@angular-eslint/template/prefer-ngsrc&quot;: &quot;error&quot;
      }
    }
  ]
}
</code></pre>
<p>Only recommended rulesets are chosen, most rules chosen are to enforce use of modern angular features.
My recommendation is to only extend these rules to prevent problems specific to your organisation.</p>
<p>Rules for selector prefix are omitted since it&#39;s redundant for single app projects.
For multi-app projects you can add config files per app, that extend from this parent config and add the rules <code>@angular-ESLint/directive-selector</code> and <code>&quot;@angular-ESLint/component-selector&quot;</code>.
This way you can enforce prefixes on components like <code>app1-component</code> and <code>app2-component</code> on a per-app basis.</p>
<h2>pre-commit</h2>
<p>Lint-staged and husky are recommended by prettier for javascript projects.
If you use a polyglot monorepo there are alternatives, like <code>pre-commit</code> and <code>husky-dotnet</code>.</p>
<pre><code class="language-json">{
  &quot;lint-staged&quot;: {
    &quot;**/*.{css,html,json,md,js,ts,yaml}&quot;: &quot;prettier --write --ignore-unknown&quot;,
    &quot;**/*.{js,ts,html}&quot;: &quot;eslint --fix&quot;
  }
}
</code></pre>
<h2>Alternatives</h2>
<p>Currently, there aren&#39;t any viable alternatives to these tools, there are in development but nothing mature yet.
Most new tooling seems to focus on speed, but I&#39;d like to see tooling that reduces the amount of setup and dependencies.</p>
<h2>Conclusion</h2>
<p>Having a decent set-up for prettier and ESLint can provide a decent basis for code quality.
By keeping the configuration minimal you can later on easily modify and extend it when specific needs arise.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Why I no longer use npx in CI</title>
      <link>https://mfstapert.dev/blog/why-i-no-longer-use-npx</link>
      <guid isPermaLink="true">https://mfstapert.dev/blog/why-i-no-longer-use-npx</guid>
      <pubDate>Tue, 02 Apr 2024 00:00:00 GMT</pubDate>
      <description>I always prefixed &quot;scripts&quot; in the scripts property of my package.json with npx. But recently I came to the conclusion this might be a bad idea, why? Let&apos;s take a look at what npx does.</description>
      <content:encoded><![CDATA[<p>I always prefixed &quot;scripts&quot; in the <code>scripts</code> property of my <code>package.json</code> with <code>npx</code>.
But recently I came to the conclusion this might be a bad idea, why? Let&#39;s take a look at what <code>npx</code> does.</p>
<pre><code>Executes &lt;command&gt; either from a local node_modules/.bin, or from a central cache, installing any packages needed in order for &lt;command&gt; to run.
</code></pre>
<p>So, when you run a script, <code>npx prettier</code> it will look in your <code>node_modules/.bin</code> first for an executable to run.
If nothing is found in your <code>node_modules</code>, <code>npx</code> will start looking for <em>any</em> version in your local cache (<code>~/.npm</code> folder on unix, <code>%LocalAppData%\npm-cache</code> on windows).
If nothing is found, it will install the latest version of the package it can find.</p>
<p>While this is great if you just want to run a standalone script: like scaffolding a new project or formatting some code, this can lead to issues in CI.
In a CI environment, you want to run commands with the versions specified in your <code>package.json</code>, nothing else.</p>
<p>If something in your pipeline breaks around the installation of <code>node_modules</code>, <code>npx</code> usage can lead to further issues.
Say you are using an older version of <code>jest</code>, but you run a script with <code>npx</code>, your test can be run with a different <code>jest</code> version than the one you&#39;ve configured.
This can lead to unintended behavior or worse, false positives.</p>
<p>Running your scripts without <code>npx</code> ensures this is not possible, your CI environment will only use the dependencies specified or break.
This seems preferable to me versus a mechanism that can potentially introduce flakiness.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Fix for custom angular formcontrol retaining input value after form reset</title>
      <link>https://mfstapert.dev/blog/bugfix-for-angular-retaining-input-value</link>
      <guid isPermaLink="true">https://mfstapert.dev/blog/bugfix-for-angular-retaining-input-value</guid>
      <pubDate>Wed, 13 Mar 2024 00:00:00 GMT</pubDate>
      <description>I came across a behavior in Angular where a custom formcontrol (using ControlValueAccessor) retained its value in the UI after being reset. Credit to this stackoverflow question, which helped me fix this issue. My post gives a concise description, fix and explanation for this behavior.</description>
      <content:encoded><![CDATA[<p>I came across a behavior in Angular where a custom formcontrol (using <code>ControlValueAccessor</code>) retained its value in the UI after being reset.
Credit to this <a href="https://stackoverflow.com/questions/76389471/angular-retaining-input-value-after-form-reset">stackoverflow question</a>, which helped me fix this issue.
My post gives a concise description, fix and explanation for this behavior.</p>
<h2>The behavior</h2>
<pre><code class="language-typescript">@Component({
    providers: [{
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() =&gt; InputRetainsComponent),
        multi: true,
    }],
    template: `
    &lt;input
      [value]=&quot;value&quot;
      (input)=&quot;onChange($event)&quot;
    /&gt;`,
})
export class InputRetainsComponent implements ControlValueAccessor
</code></pre>
<p>A component that implements <code>ControlValueAccessor</code> implements a couple of methods to interact with the Angular forms API.</p>
<pre><code class="language-typescript">onChange(event: Event): void {
    const value: string = (&lt;HTMLInputElement&gt;event.target).value;
    this.changed(value);
}
</code></pre>
<p>A <code>changed</code> method is registered as callback in this component.
Whenever the value in the ui changes the method is called and the form is notified through the callback.</p>
<pre><code class="language-typescript">writeValue(value: string): void {
    this.value = value;
}
</code></pre>
<p>On the other side you have a <code>writeValue</code> function that the form uses to write the value to the custom formcontrol.
Now if you write anything in your input in the ui and call <code>setValue</code>, the ui will update.
However, when you call <code>.reset()</code> on the form the input will retain its value.</p>
<h2>The fix</h2>
<p>The solution is to write the value directly to the view, you don&#39;t need to keep an internal <code>value</code>;</p>
<pre><code class="language-typescript">
    template: `
        &lt;input #inputRef /&gt;`,
...
  writeValue(value: string): void {
    const ref = this.inputRef();
    ref &amp;&amp; (ref.nativeElement.value = value);
  }
</code></pre>
<h2>The explanation</h2>
<p>An answer is actually in the comments of the <code>ControlValueAccessor</code> interface.</p>
<pre><code class="language-typescript">@usageNotes
### Write a value to the element

The following example writes a value to the native DOM element.

```ts
writeValue(value: any): void {
  this._renderer.setProperty(this._elementRef.nativeElement, &#39;value&#39;, value);
}
</code></pre>
<p>Though it may appear strange at first, writing directly to the DOM is actually encouraged.
Maybe custom form controls are excluded from normal change detection</p>
<p>It&#39;s still strange behavior though, the first solution only had an issue when the reset function was called.
There is a mention in the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#value">MDN docs</a> that the value attribute is used for initial value.
But when using normal input binding, the value is also mirrored...</p>
<p>If you want to look at full code examples you can find them in <a href="https://github.com/MFStapert/the_bin/tree/master/node/angular-control-retains-value">the angular-control-retains-value example on GitHub</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Warning a user of unsaved changes, in a form</title>
      <link>https://mfstapert.dev/blog/warning-a-user-of-unsaved-changes-in-angular</link>
      <guid isPermaLink="true">https://mfstapert.dev/blog/warning-a-user-of-unsaved-changes-in-angular</guid>
      <pubDate>Wed, 29 Dec 2021 00:00:00 GMT</pubDate>
      <description>Recently I had to implement a warning in our app when users leave or refresh a page when they still have unsaved changes to a form. While seemingly simple, there is some nuance I thought was worth explaining. Also some of the solutions I encountered didn&apos;t cover all my use cases, so I hope this can serve as a reference for developers in a similar situation.</description>
      <content:encoded><![CDATA[<p>Recently I had to implement a warning in our app when users leave or refresh a page when they still have unsaved changes to a form.
While seemingly simple, there is some nuance I thought was worth explaining.
Also some of the solutions I encountered didn&#39;t cover all my use cases, so I hope this can serve as a reference for developers in a similar situation.</p>
<p>The use cases I wanted to solve were as follows:</p>
<ul>
<li>This behavior should be easy to implement across multiple forms</li>
<li>This behavior should work for both empty forms and pre-filled forms (like an edit page)</li>
<li>This behavior should work with multiple formcontrols on a single page</li>
<li>A warning should be triggered on a reload or a navigation event</li>
</ul>
<p>My implementation consists of 3 pieces, a service that tracks the state of formcontrols, a directive that is used to register a new form and a guard that gets triggered on navigation event.
Al code examples are abbreviated to be more legible, a working example can be found in <a href="https://github.com/mfstapert/playground/blob/master/node/angular-form-unload/src/app/form/has-form-changed.directive.ts">the angular-form-unload example on GitHub</a></p>
<h2>HasFormChangedService</h2>
<p>This service tracks the state a formcontrol starts with and the state which the formcontrol currently has.
It does this through a simple interface:</p>
<pre><code class="language-typescript">interface FormStates {
  initialState: unknown;
  currentState: unknown;
}

class HasFormChangedService {
  private formStates: Record&lt;string, FormStates&gt; = {};
}
</code></pre>
<p>The key of the record is a unique identifier, I chose to use a uuid for this, which gets created when a form is first registered.
There are also functions to update the current form state and to set it to a new initial state.
The register function gets called later on through our directive, the directive will hold the id and handles updates.</p>
<pre><code class="language-typescript">public registerNewForm(state?: unknown): string {
	const id = uuid();
	this.formStates[id] = { initialState: state, currentState: state };
	return id;
}

public setCurrentFormState(id: string, currentState?: unknown): void {
	if (this.isFormIdNotPresent(id)) {
		throw Error(HasFormChangedService.ERROR_MSG);
	}
	this.formStates[id].currentState = currentState;
}

public setFormState(id: string, state?: unknown): void {
	if (this.isFormIdNotPresent(id)) {
		throw Error(HasFormChangedService.ERROR_MSG);
	}
	this.formStates[id] = { initialState: state, currentState: state };
}
</code></pre>
<p>With this logic you can check whether any form value has changed in the service by deep comparing the initial state with the current state.
You can use this function later on to show the user a warning.</p>
<pre><code class="language-typescript">public haveFormsBeenChanged(): boolean {
	return Object.values(this.formStates).some(
		(state) =&gt; !equals(state.initialState, state.currentState)
	);
}
</code></pre>
<p>The service also has functions to remove individual forms, or all forms from the service.
This is used when individual formcontrols get removed or when you don&#39;t want to track a form anymore, like when submiting.</p>
<h2>HasFormChangedDirective</h2>
<p>This directive is the key to easily implement this behavior across multiple forms. By adding this directive on a page and passing it a formcontrol, it registers that control to our service, when the page gets destroyed the directive also unregisters the control:</p>
<pre><code class="language-typescript">@Directive({
  selector: &#39;[hasFormChanged]&#39;,
})
export class HasFormChangedDirective implements OnInit, OnDestroy {
  @Input() public hasFormChanged!: AbstractControl;
  private formId = this.hasFormChangedService.registerNewForm();
  private subscription?: Subscription;

  constructor(private hasFormChangedService: HasFormChangedService) {}

  public ngOnInit(): void {
    this.hasFormChangedService.setFormState(this.formId, this.hasFormChanged.value);

    this.subscription = this.hasFormChanged.valueChanges
      .pipe(debounceTime(250))
      .subscribe((value) =&gt; {
        if (this.hasFormChanged.dirty) {
          this.hasFormChangedService.setCurrentFormState(this.formId, value);
          return;
        }
        // If we get valueChanges while the form is not dirty it usually means we are programmatically setting
        // the control, so we need to reset its state in order to check for changes from the user
        this.hasFormChangedService.setFormState(this.formId, value);
      });
  }

  public ngOnDestroy() {
    this.hasFormChangedService.removeForm(this.formId);
    this.subscription?.unsubscribe();
  }
}
</code></pre>
<p>There is a lot going on here, so let&#39;s unpack this :)</p>
<p>This directive gets used on a page like this <code>[hasFormChanged]=&#39;formcontrol&#39;</code>, this causes the directive to be created, before the onInit we get a formId from our service.
I had to name the formControl input the same as the name of the directive or the directive wouldn&#39;t be found, I don&#39;t know why this is.
We use this id to set the formState in our <code>OnInit</code> function, we can&#39;t do it earlier because formdata sometimes isn&#39;t loaded.
Next we subscribe to valueChanges from our formControl, we only update the current state when the control is dirty.
We had some logic that updates formControls programmatically, so this works in our situation, but it might not work in your situation.</p>
<pre><code class="language-typescript">@HostListener(&#39;window:beforeunload&#39;, [&#39;$event&#39;])
public onBeforeUnload(event: BeforeUnloadEvent): void {
	if (this.hasFormChangedService.haveFormsBeenChanged()) {
		event.preventDefault();
		event.returnValue = false;
	}
}

@HostListener(&#39;submit&#39;)
public onSubmit(): void {
	this.hasFormChangedService.clearForms();
}
</code></pre>
<p>In our application this feature only had to work for a specific module, you should probably add these functions in a more central point.
The HostListener for <code>onBeforeUnload</code> is the event that gets fired when a user triggers a reload, by checking wheter <code>haveFormsBeenChanged</code> and executing the logic a dialog gets displayed to the user.
The HostListener for <code>onSubmit</code> is to clear the forms of our service when the user submits a form, submitting a form usually triggers a redirect so we don&#39;t want to display a message in that case.</p>
<h2>HasFormChangedGuard</h2>
<p>This is the guard that gets triggered on route changes, it&#39;s the same principle as the beforeunload logic, only we use a confirmational here.
We can however display custom messages in a confirmational, so we use one from our environment.</p>
<pre><code class="language-typescript">export class HasFormChangedGuard implements CanDeactivate&lt;unknown&gt; {
  constructor(private formChangesService: HasFormChangedService) {}

  public canDeactivate(): boolean {
    return this.formChangesService.haveFormsBeenChanged()
      ? confirm(environment.hasFormChangedMessage)
      : true;
  }
}
</code></pre>
<h2>CancelFormDirective</h2>
<p>We have a seperate directive for buttons that explicitly cancel a form, which shouldn&#39;t display any message.</p>
<pre><code class="language-typescript">export class CancelFormDirective {
  constructor(private hasFormChangedService: HasFormChangedService) {}

  @HostListener(&#39;click&#39;, [&#39;$event&#39;])
  public onClick(): void {
    this.hasFormChangedService.clearForms();
  }
}
</code></pre>
<h2>Closing thoughts</h2>
<p>What I like about this solution is that it&#39;s easy to implement across multiple forms and works with forms that have multiple formgroups across multiple components.
I would like to have a solution for displaying the same type of warning to the user for both reloading as navigating.
Also I&#39;m pretty sure the HostListeners in the Directive can be moved to App.Component or a similair central point.</p>
<p>Again, the source is in <a href="https://github.com/mfstapert/playground/tree/master/node/angular-form-unload">the angular-form-unload example on GitHub</a></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
