I'm currently working on a project and trying to incorporate unit tests into it, but I'm facing difficulties in getting a specific component to be generated.
My Angular version is 15.1.3 and I can't seem to figure out what might be causing the issue. Whenever I call fixture.detectChanges();, I receive an error message saying
TypeError: Cannot read properties of undefined (reading '_hostElement')
.
Below are the sections of code that I believe are relevant:
loot-generator.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LootGeneratorComponent } from './loot-generator.component';
import { Clipboard } from '@angular/cdk/clipboard';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatCard, MatCardActions, MatCardContent, MatCardHeader, MatCardTitle } from '@angular/material/card';
import { MatSlideToggle } from '@angular/material/slide-toggle';
import { MatSlider, MatSliderVisualThumb } from '@angular/material/slider';
import { FormsModule } from '@angular/forms';
import { MatRippleModule } from '@angular/material/core';
describe('LootGeneratorComponent', () => {
let component: LootGeneratorComponent;
let fixture: ComponentFixture<LootGeneratorComponent>;
const snackBarSpy = jasmine.createSpyObj('MatSnackBar', ['open']);
const clipboardSpy = jasmine.createSpyObj('Clipboard', ['beginCopy']);
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ FormsModule, MatRippleModule ],
declarations: [
LootGeneratorComponent,
MatCard,
MatCardHeader,
MatCardTitle,
MatCardContent,
MatSlideToggle,
MatSlider,
MatCardActions,
MatSliderVisualThumb
],
providers: [
{provide: MatSnackBar, useValue: snackBarSpy},
{provide: Clipboard, useValue: clipboardSpy}
]
})
.compileComponents();
fixture = TestBed.createComponent(LootGeneratorComponent);
component = fixture.componentInstance;
fixture.detectChanges(); // Error is shown here 'TypeError: Cannot read properties of undefined (reading '_hostElement')
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
loot-generator.component.ts
import { Component } from '@angular/core';
import { CoinType } from './enums/coin.enum';
import { TreasureType } from './enums/treasure.enum';
import { Loot } from './models/loot.model';
import { TreasureGenerator } from './services/treasure-generator.service';
import { Clipboard } from '@angular/cdk/clipboard';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Coin } from './models/coin.model';
import { ICoin } from './interfaces/coin.interface';
import { Treasure } from './models/treasure.model';
@Component({
selector: 'app-loot-generator',
templateUrl: './loot-generator.component.html',
styleUrls: ['./loot-generator.component.css']
})
export class LootGeneratorComponent {
private generator: TreasureGenerator = new TreasureGenerator();
public challengeRating: number = 1;
public groupLoot: boolean = false;
public displayedColumns: string[] = ['coin', 'amount']
public displayTreasureColumns: string[] = ['treasure', 'name', 'value']
public loot: Loot = new Loot();
constructor(
private clipboard: Clipboard,
private snackBar: MatSnackBar) {}
// ... bunch 'o methods which are omitted for brevity. I doubt they are the issue as they aren't even called yet
loot-generator.html
<mat-card class="card">
<mat-card-header>
<mat-card-title>Loot Generation Options</mat-card-title>
</mat-card-header>
<mat-card-content>
<section class="options">
<div>
<mat-slide-toggle [(ngModel)]="groupLoot" name="groupLoot">Hoard Loot</mat-slide-toggle>
</div>
<div>
<mat-slider class="slider" min="1" max="20" step="1" discrete>
<input matSliderThumb [(ngModel)]="challengeRating">
</mat-slider>
<label id="slider-name-label" class="slider-name-label">CR</label>
<label id="slider-value-label" class="slider-value-label">
{{challengeRating}}
</label>
</div>
</section>
<mat-card-actions class="options-buttons">
<button
mat-flat-button
color="primary"
(click)="generateLoot()">Generate</button>
<button
mat-flat-button
color="accent"
(click)="clear()">Clear</button>
</mat-card-actions>
</mat-card-content>
</mat-card>
<mat-card class="card" *ngIf="loot.coins.length > 0 || loot.treasures.length > 0">
<mat-card-header>
<button
mat-icon-button
matTooltip="Copy to clipboard"
aria-label="Copy to clipboard"
(click)="onCopyToClipboardClick()">
<mat-icon>content_copy</mat-icon>
</button>
<button
mat-icon-button
matTooltip="Sell Loot"
aria-label="Sell Loot"
(click)="onSellClick()">
<mat-icon>sell</mat-icon>
</button>
</mat-card-header>
<mat-card class="card" *ngIf="loot.coins.length > 0">
<table
#coinTable
*ngIf="loot.coins.length > 0"
mat-table
id="0"
[dataSource]="loot.coins">
<ng-container matColumnDef="coin">
<th mat-header-cell *matHeaderCellDef> Coin </th>
<td mat-cell *matCellDef="let element"> {{getCoinName(element.type)}} </td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef> Amount </th>
<td mat-cell *matCellDef="let element"> {{element.amount}}</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef> Action </th>
<td mat-cell *matCellDef="let element">
<button mat-flat-button>Re-Roll</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</mat-card>
<mat-card class="card" *ngIf="loot.treasures.length > 0">
<table
#treasureTable
*ngIf="loot.treasures.length > 0"
mat-table
id="1"
[dataSource]="loot.treasures">
....more HTML code....
</table>
</mat-card>
<mat-card-actions>
</mat-card-actions>
</mat-card>