forked from help-me-mom/ng-mocks
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(core): providers with useExisting will be kept if their value is …
…a kept declaration help-me-mom#3778
- Loading branch information
Showing
7 changed files
with
201 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// handles forwardRef on useExisting | ||
export default (provide: any): any => { | ||
if (typeof provide === 'function' && provide.__forward_ref__) { | ||
return provide(); | ||
} | ||
|
||
return provide; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { Component, forwardRef, VERSION } from '@angular/core'; | ||
import { | ||
ControlValueAccessor, | ||
FormControl, | ||
FormsModule, | ||
NG_VALUE_ACCESSOR, | ||
ReactiveFormsModule, | ||
} from '@angular/forms'; | ||
|
||
import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; | ||
|
||
// A standalone CVA component | ||
@Component( | ||
{ | ||
selector: 'standalone-cva', | ||
template: `<input | ||
type="text" | ||
[value]="value" | ||
change="onValueChange($event.target.value)" | ||
/>`, | ||
standalone: true, | ||
providers: [ | ||
{ | ||
provide: NG_VALUE_ACCESSOR, | ||
useExisting: forwardRef(() => StandaloneCVAComponent), | ||
multi: true, | ||
}, | ||
], | ||
} as never /* TODO: remove after upgrade to a14 */, | ||
) | ||
class StandaloneCVAComponent implements ControlValueAccessor { | ||
public value = ''; | ||
|
||
onChange: any = () => undefined; | ||
onTouched: any = () => undefined; | ||
writeValue: any = () => undefined; | ||
|
||
registerOnChange(fn: (url: string) => void): void { | ||
this.onChange = fn; | ||
} | ||
|
||
registerOnTouched(fn: () => void): void { | ||
this.onTouched = fn; | ||
} | ||
|
||
setDisabledState(): void {} | ||
|
||
onValueChange(value: string): void { | ||
this.value = value; | ||
this.onChange(this.value); | ||
} | ||
} | ||
|
||
// @see https://github.com/help-me-mom/ng-mocks/issues/3778 | ||
// The problem is that StandaloneCVAComponent provides itself as NG_VALUE_ACCESSOR, | ||
// whereas NG_VALUE_ACCESSOR is going to be mocked. | ||
// The fix is to keep such NG_VALUE_ACCESSOR if its useExisting points to a kept thing. | ||
describe('issue-3791', () => { | ||
if (Number.parseInt(VERSION.major, 10) < 14) { | ||
it('needs >=a14', () => { | ||
expect(true).toBeTruthy(); | ||
}); | ||
|
||
return; | ||
} | ||
|
||
describe('issue', () => { | ||
beforeEach(() => | ||
MockBuilder([ | ||
StandaloneCVAComponent, | ||
FormsModule, | ||
ReactiveFormsModule, | ||
]), | ||
); | ||
|
||
it('does not fail on standard render', () => { | ||
const fixture = MockRender(StandaloneCVAComponent); | ||
expect(fixture.componentInstance).toBeTruthy(); | ||
}); | ||
|
||
it('does not fail as ReactiveFormsModule', () => { | ||
const fixture = MockRender( | ||
`<standalone-cva [formControl]="control"></standalone-cva>`, | ||
{ | ||
control: new FormControl('test'), | ||
}, | ||
); | ||
|
||
expect(fixture.componentInstance).toBeTruthy(); | ||
}); | ||
|
||
it('does not fail as FormsModule', () => { | ||
const fixture = MockRender( | ||
`<standalone-cva [(ngModel)]="control"></standalone-cva>`, | ||
{ | ||
value: 'test', | ||
}, | ||
); | ||
|
||
expect(fixture.componentInstance).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('.mock', () => { | ||
beforeEach(() => | ||
MockBuilder([ | ||
StandaloneCVAComponent, | ||
FormsModule, | ||
ReactiveFormsModule, | ||
]).mock(NG_VALUE_ACCESSOR), | ||
); | ||
|
||
it('provides undefined as NG_VALUE_ACCESSOR', () => { | ||
MockRender(StandaloneCVAComponent); | ||
const token = ngMocks.findInstance(NG_VALUE_ACCESSOR); | ||
expect(token).toEqual([undefined as never]); | ||
}); | ||
}); | ||
|
||
describe('.mock with a value', () => { | ||
const mock = { | ||
onChange: () => undefined, | ||
onTouched: () => undefined, | ||
writeValue: () => undefined, | ||
} as never; | ||
|
||
beforeEach(() => | ||
MockBuilder([ | ||
StandaloneCVAComponent, | ||
FormsModule, | ||
ReactiveFormsModule, | ||
]).mock(NG_VALUE_ACCESSOR, [mock]), | ||
); | ||
|
||
it('provides a mock as NG_VALUE_ACCESSOR', () => { | ||
MockRender(StandaloneCVAComponent); | ||
const token = ngMocks.findInstance(NG_VALUE_ACCESSOR); | ||
expect(token).toEqual([mock]); | ||
}); | ||
}); | ||
|
||
describe('.exclude', () => { | ||
beforeEach(() => | ||
MockBuilder([ | ||
StandaloneCVAComponent, | ||
FormsModule, | ||
ReactiveFormsModule, | ||
]).exclude(NG_VALUE_ACCESSOR), | ||
); | ||
|
||
it('removes NG_VALUE_ACCESSOR from declarations', () => { | ||
MockRender(StandaloneCVAComponent); | ||
const token = ngMocks.findInstance( | ||
NG_VALUE_ACCESSOR, | ||
undefined, | ||
); | ||
expect(token).toEqual(undefined); | ||
}); | ||
}); | ||
}); |