SQL Server  

Dissect a cluster index in the SQL server

Learning Objectives

By the end of this article, you'll learn how to:

  • Create a file upload component in Angular

  • Handle file selection using the change event

  • Store the selected file in the component

  • Write unit tests for file input controls

  • Simulate file selection in Angular unit tests

HTML Template

<input
  id="myFile"
  type="file"
  (change)="onFileSelected()"
  #fileInput
/>

Whenever the user selects a file, the change event calls the onFileSelected() method.

Component

import { Component } from '@angular/core';

@Component({
  selector: 'app-input-file',
  templateUrl: './input-file.component.html'
})
export class InputFileComponent {

  uploadedFile!: File;

  onFileSelected(): void {

    const inputNode = document.querySelector('#myFile') as HTMLInputElement;

    if (inputNode.files && inputNode.files.length > 0) {
      this.uploadedFile = inputNode.files[0];
      console.log(this.uploadedFile);
    }

  }

}

How It Works

When the user selects a file:

  • The component locates the file input element.

  • The browser stores the selected file(s) in the files collection.

  • The first file is assigned to the uploadedFile property.

Although this works, Angular recommends avoiding direct DOM access where possible.

Writing the Unit Test

The browser normally populates the files property, so during unit testing we need to mock it ourselves.

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InputFileComponent } from './input-file.component';

describe('InputFileComponent', () => {

  let component: InputFileComponent;
  let fixture: ComponentFixture<InputFileComponent>;

  beforeEach(async () => {

    await TestBed.configureTestingModule({
      declarations: [InputFileComponent]
    }).compileComponents();

    fixture = TestBed.createComponent(InputFileComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();

  });

  it('should store the selected file', () => {

    const file = new File(
      ['Dummy Content'],
      'sample.txt',
      {
        type: 'text/plain'
      }
    );

    const input = fixture.nativeElement.querySelector('#myFile');

    Object.defineProperty(input, 'files', {
      value: [file]
    });

    input.dispatchEvent(new Event('change'));

    expect(component.uploadedFile).toEqual(file);

  });

});

Understanding the Test

Step 1: Create a Mock File

const file = new File(
  ['Dummy Content'],
  'sample.txt',
  {
    type: 'text/plain'
  }
);

This creates a fake File object that behaves exactly like a file selected by the user.

Step 2: Mock the Browser's files Property

Object.defineProperty(input, 'files', {
  value: [file]
});

Since the browser owns the files property, we replace it with our mock file during testing.

Step 3: Simulate File Selection

input.dispatchEvent(new Event('change'));

This triggers the same event that occurs when a user selects a file.

Step 4: Verify the Result

expect(component.uploadedFile).toEqual(file);

The test passes if the component correctly stores the selected file.

A Better Angular Approach

Instead of querying the DOM with document.querySelector(), Angular encourages passing the event object directly.

Template

<input
  type="file"
  (change)="onFileSelected($event)"
/>

Component

onFileSelected(event: Event): void {

  const input = event.target as HTMLInputElement;

  if (!input.files?.length) {
    return;
  }

  this.uploadedFile = input.files[0];

}

This approach is:

  • More Angular-friendly

  • Easier to unit test

  • Doesn't directly access the DOM

  • Better for maintainability

Unit Test for the Improved Version

it('should store the selected file', () => {

  const file = new File(
    ['Angular Testing'],
    'document.pdf',
    {
      type: 'application/pdf'
    }
  );

  const event = {
    target: {
      files: [file]
    }
  } as unknown as Event;

  component.onFileSelected(event);

  expect(component.uploadedFile).toEqual(file);

});

Notice that we no longer need to manipulate the DOM. We simply create a mock event object and call the component method directly, making the unit test cleaner and easier to understand.

Conclusion

Unit testing file uploads in Angular is straightforward once you know how to mock the browser's files property. While older implementations often relied on document.querySelector(), modern Angular applications should use the event object passed by the change event. This results in cleaner code, simpler unit tests, and components that are easier to maintain.

The overall testing strategy is simple:

  • Create a mock File object.

  • Assign it to the input's files property (or pass it through the event object).

  • Trigger the change event.

  • Verify that the component stores the selected file correctly.

Following this approach will help you confidently test file upload functionality in any Angular application.

Summary

Testing file uploads in Angular involves simulating the browser's file selection behavior by creating mock File objects and triggering the change event. While directly accessing the DOM with document.querySelector() works, passing the event object to the component method is the recommended Angular approach because it improves maintainability, simplifies unit testing, and reduces coupling between the component and the DOM.