Testing custom element - problems with disabled.bind not updating


#1

Trying to a custom component. Simple text input and button - calls to bound search function when button pressed. Button should only be enabled when there is text entered in the input. Component works OK.

Component code:

import {bindable, inlineView} from 'aurelia-framework';

@inlineView(`
  <template>    
    <label for="searchInput">Find address</label>:
    <input id="searchInput" ref="searchInput">
    <button id="searchButton" click.trigger="search(searchInput.value)" disabled.bind="!searchInput.value">Search</button>
  </template>
`)
export class AddressFinder {
  @bindable
  search;
}

Tests (using Jest)

import {StageComponent} from 'aurelia-testing';
import {bootstrap} from 'aurelia-bootstrapper';
import {DOM} from 'aurelia-pal';
import some from 'lodash/some';

describe('AddressFinder', () => {
  let component;
  let searchFn;

  beforeEach(() => {
    searchFn = jest.fn();
    component = StageComponent.withResources('resources/elements/address/address-finder')
      .inView('<address-finder search.bind="search"></address-finder>')
      .boundTo({search: searchFn});
  });

  afterEach(() => {
    component.dispose();
  });

  it('should call search function with text from search input', async () => {
    await component.create(bootstrap);
    document.querySelector('#searchInput').value = 'Some search';
    document.querySelector('#searchButton').dispatchEvent(DOM.createCustomEvent('click'));
    expect(searchFn).toHaveBeenCalledWith('Some search');
  });

  it('should disable search button if no value entered in search input', async () => {
    await component.create(bootstrap);
    document.querySelector('#searchInput').value = '';
    const attribs = [...document.querySelector('#searchButton').attributes];
    const isDisabled = some(attribs, {name: 'disabled'});
    expect(isDisabled).toBeTruthy();
  });
});

However, if I try to add the test to check that the button is not disabled when there is content in the search input, this fails:

  it('should not disable search button when value entered in search', async () => {
    await component.create(bootstrap);
    document.querySelector('#searchInput').value = 'Some search';
    const attribs = [...document.querySelector('#searchButton').attributes];
    let isDisabled = some(attribs, {name: 'disabled'});
    expect(isDisabled).toBeFalsy();   // always true here - attribute with name 'disabled' always present
  });

Looks like the disabled attribute is always present - presumably because this is the initial value of the searchInput element.

The element value does get changed by code

document.querySelector('#searchInput').value = 'Some search';

as evidenced by the test for calling the function with the correct value from the input - that passes OK.

Any ideas how to test this? Or change the code so it is easier to test?


#2

Need to manually trigger change event - see: https://stackoverflow.com/questions/38661887/aurelia-programmatically-changing-reference-value-doesnt-change-model

  it('should not disable search button when value entered in search', async () => {
    await component.create(bootstrap);
    let input = document.querySelector('#searchInput');
    input.value = 'Some search';
    input.dispatchEvent(DOM.createCustomEvent('change'));

    let btn = document.querySelector('#searchButton');
    let disabledAttrb = {name: 'disabled'};

    expect(some([...btn.attributes], disabledAttrb)).toBeFalsy();

    input.value = '';
    input.dispatchEvent(DOM.createCustomEvent('change'));
    expect(some([...btn.attributes], disabledAttrb)).toBeTruthy();

    input.value = 'xx';
    input.dispatchEvent(DOM.createCustomEvent('change'));
    expect(some([...btn.attributes], disabledAttrb)).toBeFalsy();
  });