Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: Structural directive inheritance breaks #9655

Open
nsgundy opened this issue Aug 3, 2024 · 0 comments
Open

Bug: Structural directive inheritance breaks #9655

nsgundy opened this issue Aug 3, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@nsgundy
Copy link

nsgundy commented Aug 3, 2024

Description of the bug

Unit tests for a structural directive break when ng-mocks is used anywhere in a test (even if that test is unrelated to that directive).

This is the structural directive:

@Directive({
  selector: '[appSomeHeader]',
  standalone: true,
  inputs: [
    {name: 'foo', alias: 'appSomeHeader'},
  ],
})
export class SomeHeaderDirective extends BaseHeaderDirective {
  @Input({alias: 'appSomeHeaderBar', transform: booleanAttribute}) override bar: boolean = false;
}

The BaseHeaderDirective being extended:

@Directive({
  selector: '[appBaseHeader]',
  inputs: [{name: 'foo', alias: 'appBaseHeader'}],
  standalone: true
})
export class BaseHeaderDirective {
  foo: string = '';
  @Input({alias: 'appBaseHeaderBar', transform: booleanAttribute})
  bar: boolean = false;
}

The test being run:

describe('SomeHeaderDirective', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      imports: [HostComponent],
    }).compileComponents();
  }));

  it('should have correct foo and bar value', () => {
    const fixture = TestBed.createComponent(HostComponent);
    fixture.detectChanges();
    const debugElement = fixture.debugElement.queryAllNodes(By.directive(SomeHeaderDirective))[0];
    const directive = debugElement.injector.get(SomeHeaderDirective);
    expect(directive.foo).toBe('foo');
    expect(directive.bar).toBe(true);
  });
});

The above test works as expected, unless we introduce ng-mocks into the project. It breaks, even if ng-mocks not actively being called. Example code that will break the above test (can by in any .spec.ts file):

  afterEach(() => {
    if (false) {
      ngMocks.flushTestBed();
    }
  })

The error shown:

Error: NG0303: Can't bind to 'appSomeHeaderBar' since it isn't a known property of 'div' (used in the 'HostComponent' component template).

An example of the bug

Created a minimal reproduction here:
https://github.com/nsgundy/ng-mocks-structural-directive-inheritance-issue

Context

When I recently updated an internal project from Angular 16 to 17, a unit test making use of a structural directive that inherits from another structural directive broke. The project is using @angular/cdk's CdkHeaderRowDef that is being extended in the exact same way @angular/material's MatHeaderRowDef does it.

As far as I can see, CdkHeaderRowDef changed its sticky input definition from using the inputs property of @Directive to decorating the sticky class member via @Input while MatHeaderRowDef (we are keeping our custom component in-sync with its changes) kept overriding that input via the inputs property @Directive.

This can be worked around by changing the override to using @Input instead. See nsgundy/ng-mocks-structural-directive-inheritance-issue@fe2bd2b.

@nsgundy nsgundy added the bug Something isn't working label Aug 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant