Skip to content

Releases: getsaf/shallow-render

v6.2.1

16 Oct 22:30
Compare
Choose a tag to compare

Address backwards compatibility with NG5
See #70

v6.2.0

13 Oct 16:30
Compare
Choose a tag to compare

#64 Add support for ViewChild and ContentChild class selectors

#66 Fix typing miss in Shallow.alwaysReplaceModule

#59 There are good uses for providing a way to add module imports to an existing module.

This can now be done simply by using .import(ModuleToImport):

beforeEach(() => {
  shallow = new Shallow(MyComponent, ComponentModule).import(BrowserAnimationsModule);
});

Or, if you want to import it for all specs globally:

Shallow.alwaysImport(BrowserAnimationsModule);

Thanks @kylecannon for helping iron out the use case for this.

v6.1.3

29 Aug 22:46
Compare
Choose a tag to compare

Fix for #57

v6.1.2

11 Aug 17:40
Compare
Choose a tag to compare

Allow mocking component instance properties

When a component is written using the template-hash pattern, we sometimes need to mock methods on these components when we use them. For example:

in MyComponent we may render something like this:

<list-container #container>
  <list-item (click)="container.collapse()">Collapse the parent!</list-item>
</list-container>

Shallow will provide us a mock of list-container and list-item, but if we want to write a test that ensures we hooked up the click handler correctly, we'll have to call the collapse method on the list-container component so we'll need to mock that method out. Before this change, Shallow would not apply mock properties to component instances but now we can.

const {find} = await shallow
  .mock(ListContainerComponent, {collapse: () => undefnied})
  .render();
find('list-item').triggerEventHandler('click', {});
expect(findComponent(ListContainerComponent).collapse).toHaveBeenCalled();

Other minor features/fixes

  • feat: Shallow now includes schemas from Mocked angular modules
  • fix: Fixed an issue where some helpful error messages were showing up as [object Object] instead of the actual message

v6.1.2-0

11 Aug 17:29
Compare
Choose a tag to compare
v6.1.2-0 Pre-release
Pre-release
6.1.2-0

v6.1.1

15 Jul 17:06
Compare
Choose a tag to compare

Support for testing structural directives

Fixes #41

The general idea here is that when testing structural directives, you may simply test for the existence (or not) of the Rendering#element property.

Positive test case (just what you'd expect):

  it('shows content when value is "foo"', async () => {
    const {element} = await shallow.render(`<div *showIfFoo="'foo'">Show Me</div>`);

    expect(element.nativeElement.textContent).toBe('Show Me');
    /* -- or simply -- */
    expect(element).toBeDefined();
  });

Negative test case:

  it('does not show content when value is not "foo"', async () => {
    const {element} = await shallow.render(`<div *showIfFoo="'bar'">Show Me</div>`);

    expect(element).not.toBeDefined();
  });

v6.1.0

06 Jul 14:15
Compare
Choose a tag to compare

Wait for fixture.whenStable() on render by default

POTENTIAL TEST BREAKAGE NOTICE

It's possible that this breaks a certain category of tests. If this happens for you, the simple fix is to disable whenStable on the render for those tests. eg: shallow.render('<foo></foo>', {whenStable: false});.

It is common for components to implement OnInit and perform some sort of asynchronous initialization when the component is loaded. When using TestBed to test your component, you would generally use some sort of fakeAsync/tick combo (ugh..), or use fixture.whenStable() to wait for the async queue to be expunged.

When testing with Shallow, you generally have all your async service calls mocked out before you render your component so we could/should handle this async queue expunging automatically.

Here's a simple example of an async init:

@Component({
  selector: 'my-component',
  template: '<div *ngIf="!loading">{{data.name}}</div>'
})
class MyComponent extends OnInit {
  data: MyServiceResponse;
  get loading() { return this.data === undefined; }

  constructor(private _myService: MyService) {}

  async ngOnInit() {
    this.data = await this._myService.loadStuff();
  }
}

Our test setup:

beforeEach(() => {
  shallow = new Shallow(MyComponent, MyModule)
    .mock(MyService, {loadStuff: () => Promise.resolve({name: 'FOO'})});
});

Before this change, you'd have to handle it manually like so:

it('displays the name', async () => {
  const {find, fixture} = await shallow.render();
  await fixture.whenStable(); // boilerplate..
  fixture.detectChanges();  // more boilerplate..

  expect(find('div').nativeElement.textContent).toBe('FOO');
});

Or alternatively there's fakeAsync (I'm not a fan of this if you can't tell already):

it('displays the name', fakeAsync(async () => {
  const {find, fixture} = await shallow.render();
  tick(); // so weird!
  fixture.detectChanges();

  expect(find('div').nativeElement.textContent).toBe('FOO');
}));

We can do better:

After this change, it's a little easier:

it('displays the name', async () => {
  const {find} = await shallow.render();

  expect(find('div').nativeElement.textContent).toBe('FOO');
});

Because we now automatically wait for fixture.whenStable() we can eliminate the extra steps. Small win!

What if I want to test the component state while the async requests are pending?

You can disable the whenStable check with a parameter to the shallow.render() options:

it('does not display anything while loading', async () => {
  const {find} = await shallow.render({whenStable: false});
  
  expect(find('div')).toHaveFound(0);
});

Fix replaceModule bug that caused replacement module providers to be mocked

There was an issue that caused Shallow to mock providers on ModuleWithProviders replacement modules. This was identified in #43 (thanks @kevinbeal).

For example, using:

shallow.replaceModule(
  FooModule,
  { ngModule: FooTestModule, providers: [TestFooService] }
);

Would result in the providers array being mocked but this is never necessary when we replace modules. As a work-around, you could use .dontMock on the providers but it's a weird looking and obvious hack around this bug.

After this fix, nothing in a replacement module is ever mocked.

v6.1.0-0

06 Jul 14:17
Compare
Choose a tag to compare
v6.1.0-0 Pre-release
Pre-release
6.1.0-0

v6.0.6

11 Aug 17:28
Compare
Choose a tag to compare

Static Function Mocks!!

class MyClass {
  static reverse(thing: string) {
    return thing.split('').reverse();
  }
}

can be mocked with:

shallow.mockStatic(MyClass, {reverse: () => 'mock reverse'});

Regular objects can be mocked in this manner too:

const FOO = {
  bar: () => 'bar'
};

Can be mocked with:

shallow.mockStatic(FOO, {bar: () => 'mocked bar'});

Due to Jasmine spy limitations, only methods are supported. If you try to mock a non-method property, an error is thrown. When we begin supporting other test frameworks this limitation may go away (or only exist when using Jasmine).

class MyClass {
  static foo = 'FOO'
}

If you try to mock non-method property MyClass.foo will throw an error:

shallow.mockStatic(MyClass, {foo: 'MOCK FOO'}); // throws: InvalidStaticPropertyMockError

Support ModuleWithProviders in Shallow constructor

Sometimes, you may want to test your component's module uses the forRoot pattern. Previously, you would have to manually .provide() any service dependencies when your module used the forRoot pattern but with this fix, you can use the forRoot output directly in the Shallow constructor:

Module

@NgModule({
  declarations: [MyComponent]
})
class MyModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: MyModule,
      providers: [MyService],
  }
}

Before

shallow = new Shallow(MyComponent, MyModule)
  .provide(MyService);

After

shallow = new Shallow(MyComponent, MyModule.forRoot());

fix: Make dontMock work provider arrays

There was a bug that would prevent dontMock and neverMock from skipping mocks for providers in a forRoot array unless you used the exact same array reference in your dontMock call.

For example:

@NgModule()
class MyModule {
  static forRoot(): NgModuleWithProviders {
    return {
     ngModule: MyModule,
     providers: [FooService, BarService]
  }
}

Mocking the forRoot providers like this, would not work because each call to forRoot yields a new array reference.

shallow.dontMock(MyModule.forRoot().providers);

This fixes that bug so it works now.

v6.0.5

11 Aug 17:29
Compare
Choose a tag to compare

Two new features here:

  • Allows rendering without specifying the HTML template while also binding directly to the component properties. Thanks for the idea @ike18t!
  • Automatically spy on @Output EventEmitters

Example:

@Component({
  selector: 'my-component',
  template: '<span (click)="clickName.emit(name)">{{name}}</span>'
})
class MyComponent {
  @Input() name: string;
  @Output() clickName = new EventEmitter<string>();
}
it('can bind directly to inputs', async () => {
  const {find} = await shallow.render({bind: {name: 'FOO'}});

  expect(find('label').nativeElement.textContent).toBe('FOO');
});

it('automatically spys on @Outputs', async () => {
  const {find, instance} = await shallow.render({bind: {name: 'BAR'}});
  find('label').click();

  expect(instance.clickName.emit).toHaveBeenCalledWith('BAR');
});