Testing the addition of a dynamic class to an HTML button using Jasmine unit tests

I am brand new to Jasmine and currently in the process of grasping how to write Unit tests for my components in Angular 4. One issue I encountered is when I attempt to add a class to the button's classList within the ngOnInit() lifecycle hook of the Component, the test fails with an error stating "cannot find property 'classList' of null." This is my current approach.

Component.ts

ngOnInit() {
  document.querySelector('.button-visible').classList.add('hidden');
}

This is the scenario I'm aiming to address in my spec ts file.

Component.spec.ts

describe('AppComponent', () => {
 let component = AppComponent;
 let fixture: ComponentFixture<AppComponent>;
 let compiledElement;
});

beforeEach(async(() => {
 TestBed.configureTestingModule({
  declarations: [AppComponent]
  .....
}).compileComponents(); 
}));

beforeEach(() => {
 fixture = TestBed.createComponent(AppComponent);
 component = fixture.componentInstance;
 compiledElement = fixture.debugElement.nativeElement;
 fixture.detectChanges();
});

it('should create component', () => {
 expect(compiledElement.querySelector('button.button-visible').classList).toContain('hidden');
 expect(component).toBeTruthy();
});
});

I am struggling to determine the correct testing approach. Any assistance would be greatly appreciated.

Answer №1

When testing a component that relies on external elements, you'll need to set up a test environment that includes all necessary items. In this scenario, create a test harness with a test component containing a DOM node styled with the button-visible class.

You can easily create a test component within your test spec like so:

@Component({
  template: `
    <button class="button-visible">Test Button</button>
    <app-component></app-component>
  `,
})
class TestHostComponent {
}

Adjust your test setup to incorporate and utilize this new test component:

describe('AppComponent', () => {
  let fixture: ComponentFixture<TestHostComponent>;
  let testHostComponent: TestHostComponent;
  let component: AppComponent;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AppComponent, TestHostComponent]
      .....
    }).compileComponents(); 
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TestHostComponent);
    testHostComponent = fixture.componentInstance;
    component = fixture.debugElement.query(By.directive(AppComponent)).componentInstance;
    fixture.detectChanges();
  });
});

Run your test to confirm if the button in the test component indeed has the specified class applied:

it('should add "hidden" class to HTML elements with "button-visible" class', () => {
  const buttonElement = fixture.debugElement.query(By.css('.button-visible.hidden'));
  expect(buttonElement).toBeTruthy();
});

Answer №2

Encountering a similar challenge while writing unit tests today led me to devise the following resolution:

describe('AppComponent', () => {
  let component = AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let compiledElement;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AppComponent]
      .....
    }).compileComponents(); 
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    compiledElement = fixture.debugElement.nativeElement;
    compiledElement.innerHTML += "<button class='button-visible'>Test Button</button>";
    fixture.detectChanges();
  });

  it('should initialize component successfully', () => {
    expect(compiledElement.querySelector('button.button-visible').classList).toContain('hidden');
    expect(component).toBeTruthy();
  });
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to link function calls together dynamically using RXJS?

I am seeking a way to store the result of an initial request and then retrieve that stored value for subsequent requests. Currently, I am utilizing promises and chaining them to achieve this functionality. While my current solution works fine, I am interes ...

Tips for connecting Angular events to <input> elements in HTML?

Within my Angular application, the value of a variable is manipulated by an HTML element <input> through two-way binding like this: <input [(ngModel)]=variableName (OnKeyup)="DoSomething" > However, the two-way binding with ...

Utilizing Eithers to effectively manage errors as they propagate through the call chain

I'm delving into functional programming and exploring different ways to handle errors beyond the traditional try/catch method. One concept that has caught my attention is the Either monad in various programming languages. I've been experimenting ...

Is there a way to conceal 'private' methods using JSDoc TypeScript declarations?

If we consider a scenario where there is a JavaScript class /** * @element my-element */ export class MyElement extends HTMLElement { publicMethod() {} /** @private */ privateMethod() {} } customElements.define('my-element', MyElement) ...

How to efficiently mock the $window object in Angular unit tests

Attempting to unit test an angular custom service written in Typescript has been a challenge for me. The service is designed to read a global variable defined on the Window object and I have made it promise-based for potential future AJAX calls. Below is a ...

NativeScript: The workspace path specified does not contain a valid workspace file

Currently, I am developing a new project using NativeScript and Angular. To streamline the process, I attempted to utilize Angular generators (schematics) through the command line. The command I executed was tns generate component <component name> U ...

Issue with merging JSON in Angular using RxJS

Seeking assistance with combining two JSON objects in an Angular application. JSON Object 1: { "level1": { "level2": { "level31": "Text 1", "level32": "Text 2", "leve ...

The CSS formatting is not being properly applied within the innerHTML

I have a scenario where I am trying to display a Bootstrap card using innerHTML in my TS file, but the styles are not being applied to this content. I suspect that the issue might be because the styles are loaded before the component displays the card, cau ...

Encountering a problem with package-lock.json during project deployment

My project publishing attempts keep resulting in the same error showing up repeatedly in the logs: Any thoughts on how this issue can be resolved? 18 error Command failed: git -c core.longpaths=true add D:...\main\backend\package-lock. ...

What is causing Angular to consistently display the first object in the array on the child view, while the child .ts file correctly prints information from a different object?

Once a card of any object is clicked, the information of that specific object will be printed to the console. However, the child view will only display the details of the first object in the array it retrieves from. All pages are included below. A visual e ...

How can I send a value to an Angular element web component by clicking a button with JavaScript?

I want to update the value of an input in an Angular component by clicking on a button that is outside of the Angular Element. How can I achieve this in order to display the updated value in the UI? Sample HTML Code: <second-hello test="First Value"&g ...

The AgGridModule type does not feature the property 'ɵmod'

Recently, I decided to update my application from Angular 12 to Angular 13. The tools I am using include webpack 5, ag-grid 15.0.0, and ag-grid-angular 15.0.0. While the compilation process goes smoothly for the app, I encountered an issue when trying to l ...

How to open a print preview in a new tab using Angular 4

Currently, I am attempting to implement print functionality in Angular 4. My goal is to have the print preview automatically open in a new tab along with the print popup window. I'm struggling to find a way to pass data from the parent window to the c ...

Encountered Error: Unknown property 'createStringLiteral', causing TypeError in the Broken Storyshots. This issue is happening while using version ^8.3.0 of jest-preset-angular

When I upgraded to jest-angular-preset ^8.3.0 and tried to execute structural testing with npm run, I encountered the following error: ● Test suite failed to run TypeError: Cannot read property 'createStringLiteral' of undefined at Obje ...

What is the best approach: Developing a class or a service to handle multiple interfaces?

My current project involves creating multiple interfaces, and I would like to keep them separate for clarity. Would it be wise to do this in a service or a class? Additionally, is it possible to utilize dependency injection for classes? Thank you for your ...

Customize the position of nodes and their descendants in a d3 tree chart by setting specific x and y coordinates

I am in need of a d3 tree structure that looks like this. https://i.sstatic.net/X6U3u.png There are two key points to understand from the image above: Headers will have multiple parents(wells). I need to be able to drag and drop links connecting w ...

Can an interface be designed to have the option of containing either one property or another?

I am in need of an interface that resembles the following structure: interface EitherOr { first: string; second: number; } However, I want to make sure that this interface can only have either the property first or second. Do you think achieving this ...

Typescript is experiencing an error due to the use of attr("disabled", false) causing a disruption

Within my ts file, I'm using the code snippet below: $('input[type=hidden]').attr("disabled", false); The code functions as intended, however, an error persists: Argument of type 'false' is not assignable to parameter of typ ...

Navigating nested data structures in reactive forms

When performing a POST request, we often create something similar to: const userData = this.userForm.value; Imagine you have the following template: <input type="text" id="userName" formControlName="userName"> <input type="email" id="userEmail" ...

Error: Unable to access the 'registerControl' property of the object due to a type mismatch

I'm struggling to set up new password and confirm password validation in Angular 4. As a novice in Angular, I've attempted various approaches but keep encountering the same error. Seeking guidance on where my mistake lies. Any help in resolving t ...