CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ngx-formly--core

Core package of ngx-formly - a dynamic (JSON powered) form library for Angular that brings unmatched maintainability to your application's forms

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

testing.mddocs/

Testing Utilities

Testing utilities for creating and testing Formly components and field configurations, providing helpers for unit and integration testing scenarios.

Capabilities

Component Factory Functions

Utility functions for creating test components and field components with proper setup and configuration.

/**
 * Create a test component with specified options
 * @param options - Configuration options for the test component
 * @returns FixtureUtils with testing helpers
 */
function createComponent<T>(options: IComponentOptions<T>): FixtureUtils;

/**
 * Create a field component for testing with field configuration
 * @param field - The field configuration to test
 * @param config - Optional additional configuration
 * @returns FixtureUtils with testing helpers and field-specific methods
 */
function createFieldComponent(field: FormlyFieldConfig, config?: any): FixtureUtils;

Usage Examples:

import { createFieldComponent, createComponent } from '@ngx-formly/core/testing';
import { FormlyFieldConfig } from '@ngx-formly/core';

describe('Custom Field Component', () => {
  let fixture: FixtureUtils;

  beforeEach(() => {
    const field: FormlyFieldConfig = {
      key: 'testField',
      type: 'input',
      props: {
        label: 'Test Field',
        placeholder: 'Enter test value',
        required: true
      }
    };

    fixture = createFieldComponent(field);
  });

  it('should render field with correct label', () => {
    expect(fixture.query('label').textContent).toContain('Test Field');
  });

  it('should mark field as required', () => {
    const input = fixture.query('input');
    expect(input.hasAttribute('required')).toBe(true);
  });

  it('should update model on input change', () => {
    const input = fixture.query('input');
    input.value = 'test value';
    input.dispatchEvent(new Event('input'));
    fixture.detectChanges();
    
    expect(fixture.field.model.testField).toBe('test value');
  });
});

Custom Component Testing

import { Component } from '@angular/core';
import { FormlyFieldConfig, FieldType } from '@ngx-formly/core';
import { createComponent } from '@ngx-formly/core/testing';

@Component({
  selector: 'formly-field-custom-input',
  template: `
    <div class="custom-field">
      <label [for]="id">{{ props.label }}</label>
      <input 
        [id]="id"
        [formControl]="formControl"
        [placeholder]="props.placeholder"
        [class.error]="showError"
        (focus)="onFocus()"
        (blur)="onBlur()">
      <div *ngIf="showError" class="error-message">
        {{ getErrorMessage() }}
      </div>
    </div>
  `
})
export class CustomInputFieldComponent extends FieldType {
  onFocus() {
    console.log('Field focused');
  }

  onBlur() {
    console.log('Field blurred');
  }

  getErrorMessage(): string {
    if (this.formControl.errors?.['required']) {
      return 'This field is required';
    }
    return '';
  }
}

describe('CustomInputFieldComponent', () => {
  let fixture: FixtureUtils;

  beforeEach(() => {
    fixture = createComponent({
      component: CustomInputFieldComponent,
      fieldConfig: {
        key: 'customInput',
        type: 'custom-input',
        props: {
          label: 'Custom Input',
          placeholder: 'Enter value',
          required: true
        }
      }
    });
  });

  it('should display custom styling', () => {
    const customField = fixture.query('.custom-field');
    expect(customField).toBeTruthy();
  });

  it('should show error message when invalid', () => {
    fixture.field.formControl.markAsTouched();
    fixture.detectChanges();
    
    const errorMessage = fixture.query('.error-message');
    expect(errorMessage.textContent).toContain('This field is required');
  });

  it('should handle focus and blur events', () => {
    spyOn(fixture.component, 'onFocus');
    spyOn(fixture.component, 'onBlur');
    
    const input = fixture.query('input');
    input.dispatchEvent(new Event('focus'));
    input.dispatchEvent(new Event('blur'));
    
    expect(fixture.component.onFocus).toHaveBeenCalled();
    expect(fixture.component.onBlur).toHaveBeenCalled();
  });
});

Types

Testing Configuration Types

interface IComponentOptions<T> {
  /** The component class to test */
  component: Type<T>;
  
  /** Field configuration for the component */
  fieldConfig?: FormlyFieldConfig;
  
  /** Form model data */
  model?: any;
  
  /** Form options */
  options?: FormlyFormOptions;
  
  /** Additional providers for testing */
  providers?: Provider[];
  
  /** Additional imports for the test module */
  imports?: any[];
  
  /** Additional declarations for the test module */
  declarations?: any[];
}

interface FixtureUtils {
  /** Angular ComponentFixture instance */
  fixture: ComponentFixture<any>;
  
  /** Component instance */
  component: any;
  
  /** Field configuration used in the test */
  field: FormlyFieldConfig;
  
  /** Form model data */
  model: any;
  
  /** Form instance */
  form: FormGroup;
  
  /** Trigger change detection */
  detectChanges(): void;
  
  /** Query for DOM element by selector */
  query<T extends Element = Element>(selector: string): T;
  
  /** Query for all DOM elements by selector */
  queryAll<T extends Element = Element>(selector: string): T[];
  
  /** Set field value and trigger change detection */
  setFieldValue(key: string, value: any): void;
  
  /** Check if field has specific error */
  hasFieldError(key: string, errorType: string): boolean;
  
  /** Get field error message */
  getFieldError(key: string): string | null;
}

Mock Data Types

/**
 * Helper type for creating mock field configurations
 */
interface MockFieldConfig extends Partial<FormlyFieldConfig> {
  key: string;
  type: string;
}

/**
 * Helper interface for mock form data
 */
interface MockFormData {
  [key: string]: any;
}

/**
 * Configuration for creating test scenarios
 */
interface TestScenario {
  name: string;
  fieldConfig: FormlyFieldConfig;
  initialModel?: any;
  expectedValue?: any;
  actions?: Array<{
    type: 'input' | 'select' | 'click' | 'focus' | 'blur';
    selector: string;
    value?: any;
  }>;
}

Import

import { 
  createComponent, 
  createFieldComponent,
  FixtureUtils,
  IComponentOptions 
} from '@ngx-formly/core/testing';

Test Setup

Basic Test Module Configuration

import { TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { FormlyModule } from '@ngx-formly/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [
      ReactiveFormsModule,
      NoopAnimationsModule,
      FormlyModule.forRoot({
        types: [
          { name: 'input', component: FormlyFieldInput },
          { name: 'select', component: FormlyFieldSelect },
          { name: 'textarea', component: FormlyFieldTextarea }
        ]
      })
    ]
  }).compileComponents();
});

Advanced Testing Examples

Form Validation Testing

describe('Form Validation', () => {
  let fixture: FixtureUtils;

  beforeEach(() => {
    const fields: FormlyFieldConfig[] = [
      {
        key: 'email',
        type: 'input',
        props: {
          label: 'Email',
          type: 'email',
          required: true
        },
        validators: {
          email: {
            validation: Validators.email,
            message: 'Please enter a valid email address'
          }
        }
      },
      {
        key: 'password',
        type: 'input',
        props: {
          label: 'Password',
          type: 'password',
          required: true,
          minLength: 8
        }
      }
    ];

    fixture = createComponent({
      fields,
      model: {}
    });
  });

  it('should show validation errors for invalid email', () => {
    fixture.setFieldValue('email', 'invalid-email');
    
    expect(fixture.hasFieldError('email', 'email')).toBe(true);
    expect(fixture.getFieldError('email')).toContain('valid email address');
  });

  it('should validate password length', () => {
    fixture.setFieldValue('password', '123');
    
    expect(fixture.hasFieldError('password', 'minlength')).toBe(true);
  });

  it('should be valid with correct values', () => {
    fixture.setFieldValue('email', 'user@example.com');
    fixture.setFieldValue('password', 'securepassword123');
    
    expect(fixture.form.valid).toBe(true);
  });
});

Dynamic Field Testing

describe('Dynamic Fields', () => {
  it('should show/hide fields based on conditions', () => {
    const fields: FormlyFieldConfig[] = [
      {
        key: 'hasAccount',
        type: 'checkbox',
        props: {
          label: 'I have an existing account'
        }
      },
      {
        key: 'username',
        type: 'input',
        props: {
          label: 'Username'
        },
        expressions: {
          hide: '!model.hasAccount'
        }
      }
    ];

    const fixture = createComponent({ fields, model: {} });

    // Initially username should be hidden
    expect(fixture.query('input[id*="username"]')).toBeFalsy();

    // Check the checkbox
    fixture.setFieldValue('hasAccount', true);
    fixture.detectChanges();

    // Now username should be visible
    expect(fixture.query('input[id*="username"]')).toBeTruthy();
  });
});

Custom Validator Testing

function customValidator(control: AbstractControl): ValidationErrors | null {
  if (control.value && control.value.length < 5) {
    return { tooShort: { actualLength: control.value.length, requiredLength: 5 } };
  }
  return null;
}

describe('Custom Validator', () => {
  it('should validate with custom validator', () => {
    const field: FormlyFieldConfig = {
      key: 'customField',
      type: 'input',
      validators: {
        custom: {
          validation: customValidator,
          message: 'Value must be at least 5 characters long'
        }
      }
    };

    const fixture = createFieldComponent(field);
    
    fixture.setFieldValue('customField', '123');
    
    expect(fixture.hasFieldError('customField', 'tooShort')).toBe(true);
    expect(fixture.getFieldError('customField')).toContain('5 characters long');
  });
});

docs

configuration.md

field-components.md

field-types.md

index.md

json-schema.md

select.md

services.md

testing.md

utilities.md

validation.md

tile.json