Saturday, November 19, 2022

Testing with JsonPatchDocument in Asp.Net

I've written in the past about how to use the PATCH verb for updating individual properties on larger objects in a RESTful way. When it comes time to test those controller methods, it helps to have a way to reliably setup your test data. That's what this post is about.

I created a static class that I can use from my tests to create a JsonPatchDocument to pass to your tests. Here's what that class looks like:

   1:  using Microsoft.AspNetCore.JsonPatch;
   2:  using System;
   3:  using System.Linq.Expressions;
   4:  
   5:  namespace Blog.UnitTests.Utilities.Setup
   6:  {
   7:      public static class JsonPatchDocumentSetup
   8:      {
   9:          public static JsonPatchDocument<T> BuildJsonPatchDocument<T, TProp>(Expression<Func<T, TProp>> expr, TProp newObject, string type) where T : class
  10:          {
  11:              var jsonPatchDocument = new JsonPatchDocument<T>();
  12:  
  13:              switch (type)
  14:              {
  15:                  case "add":
  16:                      jsonPatchDocument.Add(expr, newObject);
  17:                      break;
  18:                  case "copy":
  19:                      jsonPatchDocument.Copy(expr, expr);
  20:                      break;
  21:                  case "move":
  22:                      jsonPatchDocument.Move(expr, expr);
  23:                      break;
  24:                  case "remove":
  25:                      jsonPatchDocument.Remove(expr);
  26:                      break;
  27:                  case "replace":
  28:                      jsonPatchDocument.Replace(expr, newObject);
  29:                      break;
  30:              }
  31:  
  32:              return jsonPatchDocument;
  33:          }
  34:  
  35:          public static JsonPatchDocument<T> AppendToJsonPatchDocument<T, TProp>(this JsonPatchDocument<T> jsonPatchDocument, Expression<Func<T, TProp>> expr,
                                                TProp newObject, string type) where T : class
  36:          {
  37:              switch (type)
  38:              {
  39:                  case "add":
  40:                      jsonPatchDocument.Add(expr, newObject);
  41:                      break;
  42:                  case "copy":
  43:                      jsonPatchDocument.Copy(expr, expr);
  44:                      break;
  45:                  case "move":
  46:                      jsonPatchDocument.Move(expr, expr);
  47:                      break;
  48:                  case "remove":
  49:                      jsonPatchDocument.Remove(expr);
  50:                      break;
  51:                  case "replace":
  52:                      jsonPatchDocument.Replace(expr, newObject);
  53:                      break;
  54:              }
  55:  
  56:              return jsonPatchDocument;
  57:          }
  58:      }
  59:  }

And here are some examples of how you'd use it:

   1:  using Blog.BusinessLogic.Implementations;
   2:  using System.Collections.Generic;
   3:  using System.Threading.Tasks;
   4:  using Xunit;
   5:  
   6:  namespace Blog.BusinessLogic.UnitTests.Implementations
   7:  {
   8:      public class BlogClassUnitTests
   9:      {
  10:          ...test class setup...
  11:          [Theory]
  12:          [InlineData(true, true)]
  13:          [InlineData(true, false)]
  14:          [InlineData(true, null)]
  15:          [InlineData(null, true)]
  16:          [InlineData(null, false)]
  17:          [InlineData(null, null)]
  18:          [InlineData(false, true)]
  19:          [InlineData(false, false)]
  20:          [InlineData(false, null)]
  21:          public async Task DoingTheThingShouldDoWhatIsExpectedUsingInlineData(bool? firstUpdatedValue, bool? secondUpdatedValue)
  22:          {
  23:              // arrange
  24:              var jsonPatchDocument = JsonPatchDocumentSetup.BuildJsonPatchDocument<UpdateableViewModel, bool?>(p => p.FirstUpdateableProperty, firstUpdatedValue, "replace");
  25:              jsonPatchDocument.AppendToJsonPatchDocument(p => p.SecondUpdateableProperty, secondUpdatedValue, "replace");
  26:              ...additional setup...
  27:  
  28:              // act
  29:              ...take action...
  30:  
  31:              // assert
  32:              ...make assertions...
  33:          }
  34:  
  35:          public static IEnumerable<object[]> BlogTestCases
  36:          {
  37:              get
  38:              {
  39:                  return new[]
  40:                  {
  41:                      new object[]
  42:                      {
  43:                          new UpdateableViewModel
  44:                          {
  45:                              FirstUpdateableProperty = true
  46:                              NestedUpdateableProperty = new NestedUpdateableViewModel()
  47:                              SecondUpdateableProperty = false
  48:                          },
  49:                          (Expression<Func<UpdateableViewModel, NestedUpdateableViewModel>>)(p => p.NestedUpdateableProperty),
  50:                          new NestedUpdateableViewModel{Id = 987},
  51:                          new UpdateableViewModel
  52:                          {
  53:                              FirstUpdateableProperty = true
  54:                              NestedUpdateableProperty = new NestedUpdateableViewModel{Id = 987}
  55:                              SecondUpdateableProperty = false
  56:                          }
  57:                      }
  58:                  }
  59:              }
  60:          }
  61:  
  62:          [Theory, MemberData(nameof(BlogTestCases))]
  63:          public async Task DoingTheThingShouldDoWhatIsExpectedUsingMemberData<TProp1, TProp2>(UpdateableViewModel currentViewModel, Expression<Func<UpdateableViewModel, TProp1>> expression, TProp1 newObject, UpdateableViewModel expected)
  64:          {
  65:              // arrange
  66:              var jsonPatchDocument = JsonPatchDocumentSetup.BuildJsonPatchDocument&lt(expression, newObject, "replace");
  67:              ...additional setup...
  68:  
  69:              // act
  70:              ...take action...
  71:  
  72:              // assert
  73:              ...make assertions...
  74:          }
  75:      }
  76:  }

Hopefully next time I need this I remember I wrote this post, or maybe somebody else comes across this and it helps them with unit testing their own code. Maybe some day I'll turn this into a NuGet package. Ah, to dream.

Friday, July 22, 2022

Dynamic QueryParams with RouterLink

This is something that should have been obvious, but still required me to find an answer on SO. As I do, I'm putting it here to make it easier to find in the future. Basically I wanted to use an anchor tag with the routerLink directive, but send out dynamic parameters and I couldn't figure out how to do it. It turns out you can just bind the queryParams part of routerLink to a property on your component and pass it that way.

component.ts

<snip>...<snip>
public dynamicQueryParams: Params;
<snip>...<snip>
this.dynamicQueryParams = {first: 'value', second: 'empty'};
<snip>...<snip>

component.html

<snip>...<snip>
<a [routerLink]="['/support/case-history']" [queryParams]="dynamicQueryParams" target="_blank">Click Me!</a>
<snip>...<snip>
That's pretty much it. Obviously, you'll need to build your dynamicQueryParams variable the way you want it, but this is how you can pass dynamic parameters via a routerLink directive.

Monday, July 18, 2022

No value accessor for form control with name

 I'm going to be brief here and hopefully circle back to this with more details later. As of today (July 18, 2022) there is an open issue in the Angular Github repository stating the team should try to improve this error message. I wish they would because today I wasted two hours on something really stupid. Ultimately, it was my fault for doing the wrong thing, but the whole point of error messages should be to help you find and fix the error.

Anyway, what happened to me today is I was using a 3rd party component (Kolkov Angular Editor) and it was throwing an error that originally said "No value accessor for form control with unspecified name attribute". After some trial and error and moving a bunch of stuff around I thought I had identified that the problem was caused by the 3rd party control itself and there was some bug in it. I applied a workaround that fixed it in one place, but not the other. Eventually, I was able to piece together that I was importing the 3rd party module in my wrong module.

My app is broken up (as it should be) into separate modules by purpose. Then I have one SharedModule where I declare, import, and export anything that's used across multiple modules in my app. When I originally built the piece that uses the Kolkov Angular Editor I had it all in one module, but as my app grew I moved some of my components into my SharedModule, but left the importation of the Kolkov module in its original place. That was the root of my problem. Once I moved the import (and export) into my SharedModule everything worked perfectly. And it only took me two hours to figure out.

Monday, May 23, 2022

No provider for ControlContainer!

As you can probably deduce from the title, this post is all about an annoying error in Angular that reads "No provider for ControlContainer!". If you're just getting this error in general, the first thing you should do is make sure you've imported the FormsModule and ReactiveFormsModule (yes, both of them). That will probably clear it up for you. If you've done that and you're still getting this error, specifically while running your unit tests, I have a solution for you. I'm nearly 100% certain I didn't come up with this on my own, but since I'm not sure where I originally found it I can't link you over to it. Sorry for that.

First off, let me show you the code that triggered this issue when I started testing. Like a good programmer following DRY (Don't Repeat Yourself) I will often componentize even small things that are reused and require some bit of setup or configuration. For instance, when creating forms that require masked input fields (like phone number or credit card) I'll create a component that allows me to quickly and easily drop that into my form and just specify which mask to use. The way I do that requires me to inject ControlContainer directly into the component. Here's some example code:

import { Component, Input, OnInit } from '@angular/core';
import { ControlContainer, FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-masked-input'
  templateUrl: './masked-input.html'
})
export class MaskedInputComponent implements OnInit {
  @Input() controlName: string;
  @Input() label: string;
  @Input() mask: any;

  constructor(public controlContainer: ControlContainer) { }

  public ngOnInit(): void {
    // Set our form property to the parent control
    // (i.e. FormGroup) that was passed to us, so that our
    // view can data bind to it
    this.form = this.controlContainer.control as FormGroup;
    this.control = this.form.get(this.controlName) as FormControl;
  }
}

That's the relevant part to this error. If we try to run unit tests around that code we'll get the error "No provider for ControlContainer!" even if we import FormsModule and ReactiveFormsModule in our test file. To fix that error, we have to manually create a FormGroupDirective in our test file and change the provider for ControlContainer to use that FormGroupDirective. Here are the relevant bits of code:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlContainer, FormControl, FormGroup, FormGroupDirective, FormsModule, ReactiveFormsModule } from '@angular/forms';
...snip...
describe('MaskedInputComponent'), () => {
  let component: MaskedInputComponent;
  let fixture: ComponentFixture<MaskedInputComponent>;
  const formGroup: FormGroup = new FormGroup({
    dynamicControlName: new FormControl('')
  });
  const formGroupDirective: FormGroupDirective = new FormGroupDirective([], []);
  formGroupDirective.form = formGroup;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ MaskedInputComponent ],
      imports: [ FormsModule, ReactiveFormsModule ],
      providers: [ { provide: ControlContainer, useValue: formGroupDirective } ],
    })
    compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(MaskedInputComponent);
      component = fixture.componentInstance;
      component.controlName = 'dynamicControlName';
      fixture.detectChanges();
    })
    compileComponents();
  });
});

That's it! This will alleviate the "No provider for ControlContainer!" error in your test run. It's simple enough once you know what to do, but it was a pain figuring it out. Hope this helps.

Friday, March 25, 2022

rxjs EMPTY

I know the blog is technically called "Answers I Couldn't Find Anywhere Else" and I did find this answer somewhere else, but it's a very valuable answer that I want to keep for future reference. 

This other blog helped me understand what was going on with my unit tests and why forkJoin wasn't being triggered when I used EMPTY.

The TL;DR of it is that EMPTY immediately emits a complete without ever emitting a value, but forkJoin fires when all observables emit their value. Since EMPTY never emits a value, forkJoin never fires. Simple as that.

Friday, February 18, 2022

Printing with Angular

I'm in the middle of migrating an older project from Angular 7 to Angular 12. My team made a decision to start from scratch and build the application up using good patterns and practices this time (the original project had to be thrown together rather quickly to meet a business deadline; so it goes). This week I arrived at a piece of code intended to print a receipt. The receipt itself is an isolated component that is typically (but not always) displayed in a modal (we use ng-bootstrap). In the old version we have a rather sloppy (but effective) piece of code that opens a new window and writes the HTML contents of the receipt into the new window, then immediately invokes the print function. It does work, but man is it unseemly. At first I tried to just migrate that code over, but the newer version of TypeScript didn't like some things about it (and I didn't spend much time trying to figure out what it didn't like). For posterity, here's the old code.


print() {
  let printContents, popupWin;
  printContents = document.getElementById('print-section').innerHTML
  printContents = `<div div class="text-center mb-4 mt-4"><img src="https://logo.url" class="receipt-logo"></div>${printContents}`;
  popupWin = window.open('/', '_blank', 'top=0,left=0,height=100%,width=auto');
  popupWin.document.open();
  popupWin.document.write(`
    <html>
      <head>
        <title>Receipt</title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
        <style>
          @page {
            size: auto;   /* auto is the initial value */
            margin: 5%;
          }
          body {
            padding-top: 50px;
          }
          .btn {
            display: none;
          }
          .text-primary {
            color: #333333;
          }
          .receipt-logo {
            width: 200px;
          }
        </style>
      </head>
      <body onload="window.print();setTimeout(function() { window.close(); }, 500); // Timout is Safari hack. Safari dosn't wait for print to close window">${printContents}</body>
    </html>
  );
  popupWin.document.close();
}

I want to stress once more that this absolutely does work, but... well, just look at it. It's unpleasant. Since we're taking the time to rewrite the entire application I wanted a better way to do this, but Angular sure didn't make it easy.

The TL;DR of what I did is pretty simple: I created a component that injects another component into a directive that sits as a sibling to my root component.

I'll first acknowledge that what I'm doing here is so common that the Angular docs actually have a tutorial on how to do it, which you can find here. I had to make some modifications to that code to make it work for me, which I based on a previous blog post I created, which you can find here.

Now for the details. There are a few factors I had to consider.
  1. The component's contents must be printable while the component is hosted in a modal and while it is hosted outside a modal
  2. I don't want to reload the entire contents of the receipt just so I can print it, but I'm OK with using data that's in state (which is how the application is constructed) to generate new HTML
  3. I want to be able to control what is printed on the receipt, and potentially have that be different than what is displayed in the UI
My app.component is pretty basic, but here's what it looked like before I added in my print stuff:

template: `<router-outlet></router-outlet><app-footer></app-footer>`

Side note: within the <router-outlet> the very first component will always be <app-wrapper>. I did that because I have a public section and a secure (behind a login) section of my app. Each of those sections has a different wrapper, but both use the <app-wrapper> selector.

The first thing I need is a directive that will host my receipt component.

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appPrint]'
})
export class PrintDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

Next, I need a service so I know which component will be printed (this could be eliminated if you only always want to print the same component, but I already know that I'll have another component down the road that will need to be printed as well).

import { Injectable, Type } from '@angular/core';

import { BehaviorSubject } from 'rxjs';
import { v4 as uuidv4 } from 'uuid';

import { PrintableComponent } from '../../../data/models';

@Injectable({
  providedIn: 'root'
})
export class PrintStateService {
  public printNotifier: BehaviorSubject<string | null> = new BehaviorSubject<string | null>(null);
  public type?: Type<PrintableComponent>;

  public clear(): void {
    this.type = undefined;
    this.printNotifier.next(null);
  }

  public set(type: Type<PrintableComponent>): void {
    this.type = type;
    this.printNotifier.next(uuidv4());
  }
}

Now that I have the directive and the service I need the component to actually instantiate the component and plug it into the directive.

import { AfterViewInit, Component, ComponentFactoryResolver, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core'

import { Subject } from 'rxjs'
import { takeUntil } from 'rxjs/operators';

import { PrintStateService } from '../../../core';
import { PrintDirective } from '../../directives/print.directive';


  
@Component({
  selector: 'app-print',
  template: '<ng-template atlasPrint><ng-template>',
  styleUrls: ['./print-component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class PrintComponent implements AfterViewInit, OnDestroy {
  private takeSubscriptionsUntil: Subject<void> = new Subject();

  @ViewChild(PrintDirective, {static: true}) printHost!: PrintDirective;

  constructor (
    private componentFactoryResovler: ComponentFactoryResolver,
    private printStateService: PrintStateService
  ) { }

  public ngAfterViewInit(): void {
    this.printStateService.printNotifier.pipe(takeUntil(this.takeSubscriptionsUntil))
      .subscribe((notificationId: string | null) => {
        if (!!notificationId) {
          this.loadComponent();
        } else {
          this.printHost.viewContainerRef.clear();
        }
      })
  }

  public ngOnDestroy(): void {
    this.takeSubscriptionsUntil.next();
    this.takeSubscriptionsUntil.complete();
  }

  private loadComponent(): void {
    if (!this.printStateService.type( {
      return;
    }

    const viewContainerRef = this.printHost.viewContainerRef;
    viewContainerRef.clear();

    const factory = this.componentFactoryResolver.resolveComponentFactory(this.printStateService.type);
    const componentRef = viewContainerRef.createComponent(factory);
    componentRef.instance.isPrinted = true;

    // this is wrapped in a setTimeout because not doing that causes the data to not be loaded into the injected component
    // so basically wrapping it like this gives the lifecycle the necessary order to populate the injected component so there's
    // actually something to display in the print preview window
    setTimeout(() => window.print(), 1);
  }
}

A little bit of styling on the print component allows us to hide everything except the print component when printing is invoked.

@media screen {
  app-print {
    display: none !important;
  }
}

@media print {
  .modal, .modal-backdrop, .app-wrapper, .app-footer, button {
    display: none !important;
  }
}

Once I have all of that in place I can modify the app.component to use the print component I created in the previous step.

template: `<router-outlet></router-outlet><app-print></app-print><app-footer></app-footer>`

The only thing left now is to tell the print button on the receipt component to load the receipt component anew into the print component and trigger the browser's print functionality.

import { Component, Input, OnDestroy, OnInit } from '@angular/core'

import { Subject } from 'rxjs'
import { takeUntil } from 'rxjs/operators';

import { OrderStatuses, OrderTypes, PaymentTypes } from '../../../data/enums';
import { LoadingStateService, PrintStateService, ReceiptStateService } from '../../../core';
import { PrintableComponent, Receipt } from '../../data/models';


  
@Component({
  selector: 'app-receipt',
  templateUrl: './receipt.component.html',
  styleUrls: ['./receipt-component.scss']
})
export class ReceiptComponent implements OnInit, OnDestroy, PrintableComponent {
  @Input() isModal: boolean = false;
  @Input() isPrinted: boolean = false;

  private takeSubscriptionsUntil: Subject<void> = new Subject();

  public receipt: Receipt | null | undefined;
  public isLoading = false;
  public OrderStatuses: typeof OrderStatuses = OrderStatuses;
  public OrderTypes: typeof OrderTypes = OrderTypes;
  public PaymentTypes: typeof PaymentTypes = PaymentTypes;

  constructor (
    private printStateService: PrintStateService,
    private receiptStateService: ReceiptStateService
  ) { }

  public ngOnInit(): void {
    this.receiptStateService.stateChanged.pipe(takeUntil(this.takeSubscriptionsUntil))
      .subscribe((state: StoreState) => state?.receipt);

  }

  public ngOnDestroy(): void {
    this.takeSubscriptionsUntil.next();
    this.takeSubscriptionsUntil.complete();
  }

  public print(): void {
    this.printStateService.set(ReceiptComponent);
  }
}

Oops, I forgot to show what the PrintableComponent interface looks like. Here it is.

export interface PrintableComponent {
  isPrinted: boolean;
}

Et voila! Clicking the print button on the receipt pushes a new instance of the receipt component into the print directive on the print component, then triggers the browser's print functionality through window.print. Pretty neat!