Skip to content

Proposal: new way to spy output without any assertion #462

Closed
@lacolaco

Description

@lacolaco

Context

I think that there is some pains in spying on the output of a component, which is typified by the fact that you have to use type assertions by any. It is my opinion that the cost to pay for simply subscribing to events emitted by a component and inspecting their values can be lesser.

https://github.com/testing-library/angular-testing-library/blob/main/apps/example-app/src/app/examples/22-signal-inputs.component.spec.ts#L51-L66

test('output emits a value', async () => {
  const submitFn = jest.fn();
  await render(SignalInputComponent, {
    componentInputs: {
      greeting: 'Hello',
      name: 'world',
    },
    componentOutputs: {
      submit: { emit: submitFn } as any,  // <==
    },
  });

  await userEvent.click(screen.getByRole('button'));

  expect(submitFn).toHaveBeenCalledWith('world');
});

Goals

  • No any assertions in output testing scenario
  • Less and intuitive code to subscribe outputs

Idea

This is a pseudo code and has not been strictly validated for feasibility.

test('output emits a value', async () => {
  const submitFn = jest.fn();
  const { outputs } = await render(SignalInputComponent, {
    componentInputs: {
      greeting: 'Hello',
      name: 'world',
    },
  });
  outputs.get('submit').subscribe(submitFn);

  await userEvent.click(screen.getByRole('button'));

  expect(submitFn).toHaveBeenCalledWith('world');
});
  • Added outputs to RenderResult
  • outputs.get(name) can get a output ref by alias-aware name

outputs.get(eventName)

The eventName should be same to its name in HTML template, <some-cmp (eventName)="..." />.
Angular's ComponentMirror has outputs getter which returns a record of propName and tempalteName of outputs. So I think ALT can map the eventName to corresponding property.

https://github.com/angular/angular/blob/main/packages/core/src/render3/component.ts#L104-L139

// pseudo code to get OutputRef from eventName
function getOutputRef<C, T>(componentRef: ComponentRef<C>, eventName: string): OutputRef<T> {
  const { outputs } = reflectComponentType(componentRef.componentType);
  const output = outputs.find(o => o.propName === eventName);
  if (output == null) {
    throw new Error(`Output ${eventName} doesn't exist.`);
  }
  return componentRef.instance[output.templateName];
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions