Skip to content

Commit ec079c2

Browse files
Address review feedback: Remove re-exports, enable mixed usage with warnings, remove v20 references
Co-authored-by: timdeschryver <[email protected]>
1 parent 3dca2ea commit ec079c2

File tree

5 files changed

+74
-16
lines changed

5 files changed

+74
-16
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,13 @@ describe('Counter', () => {
151151
});
152152
```
153153

154-
### Angular v20+ Bindings API
154+
### Angular Bindings API
155155

156-
Angular Testing Library also supports Angular v20's native bindings API, which provides a more direct way to bind inputs and outputs:
156+
Angular Testing Library also supports Angular's native bindings API, which provides a more direct way to bind inputs and outputs:
157157

158158
```typescript
159-
import { render, screen, inputBinding, outputBinding } from '@testing-library/angular';
159+
import { render, screen } from '@testing-library/angular';
160+
import { inputBinding, outputBinding } from '@angular/core';
160161
import { CounterComponent } from './counter.component';
161162

162163
describe('Counter with Bindings API', () => {

projects/testing-library/src/lib/models.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ export interface RenderComponentOptions<ComponentType, Q extends Queries = typeo
310310

311311
/**
312312
* @description
313-
* An array of bindings to apply to the component using Angular v20+'s native bindings API.
313+
* An array of bindings to apply to the component using Angular's native bindings API.
314314
* This provides a more direct way to bind inputs and outputs compared to the `inputs` and `on` options.
315315
*
316316
* @default

projects/testing-library/src/lib/testing-library.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,35 @@ export async function render<SutType, WrapperType = SutType>(
196196
): Promise<ComponentFixture<SutType>> => {
197197
const createdFixture: ComponentFixture<SutType> = await createComponent(componentContainer, bindings);
198198

199-
// Only use traditional input/output setting if no bindings are provided
200-
// When bindings are used, they handle inputs and outputs natively
201-
if (!bindings || bindings.length === 0) {
202-
setComponentProperties(createdFixture, properties);
203-
setComponentInputs(createdFixture, inputs);
199+
// Always apply componentProperties (non-input properties)
200+
setComponentProperties(createdFixture, properties);
201+
202+
// Angular doesn't allow mixing setInput with bindings
203+
// So we use bindings OR traditional approach, but not both for inputs
204+
if (bindings && bindings.length > 0) {
205+
// When bindings are used, warn if traditional inputs/outputs are also specified
206+
if (Object.keys(inputs).length > 0) {
207+
console.warn(
208+
'ATL: You specified both bindings and traditional inputs. ' +
209+
'Angular does not allow mixing setInput() with inputBinding(). ' +
210+
'Only bindings will be used for inputs. Use bindings for all inputs to avoid this warning.',
211+
);
212+
}
213+
if (Object.keys(subscribeTo).length > 0) {
214+
console.warn(
215+
'ATL: You specified both bindings and traditional output listeners. ' +
216+
'Consider using outputBinding() for all outputs for consistency.',
217+
);
218+
}
219+
220+
// Only apply traditional outputs, as bindings handle inputs
204221
setComponentOutputs(createdFixture, outputs);
205222
subscribedOutputs = subscribeToComponentOutputs(createdFixture, subscribeTo);
206223
} else {
207-
// With bindings, we still need to handle componentProperties for non-input properties
208-
setComponentProperties(createdFixture, properties);
224+
// Use traditional approach when no bindings
225+
setComponentInputs(createdFixture, inputs);
226+
setComponentOutputs(createdFixture, outputs);
227+
subscribedOutputs = subscribeToComponentOutputs(createdFixture, subscribeTo);
209228
}
210229

211230
if (removeAngularAttributes) {

projects/testing-library/src/public_api.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,3 @@
55
export * from './lib/models';
66
export * from './lib/config';
77
export * from './lib/testing-library';
8-
9-
// Re-export Angular's binding functions for convenience
10-
export { inputBinding, outputBinding, twoWayBinding, type Binding } from '@angular/core';

projects/testing-library/tests/bindings-support.spec.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Component, input, output } from '@angular/core';
2-
import { render, screen, inputBinding, outputBinding } from '../src/public_api';
1+
import { Component, input, output, inputBinding, outputBinding } from '@angular/core';
2+
import { render, screen, aliasedInput } from '../src/public_api';
33

44
describe('ATL Bindings API Support', () => {
55
@Component({
@@ -38,4 +38,45 @@ describe('ATL Bindings API Support', () => {
3838

3939
expect(clickHandler).toHaveBeenCalledWith('clicked: bound-value');
4040
});
41+
42+
it('should warn when mixing bindings with traditional inputs but still work', async () => {
43+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
44+
const clickHandler = jest.fn();
45+
const bindingClickHandler = jest.fn();
46+
47+
await render(BindingsTestComponent, {
48+
bindings: [inputBinding('value', () => 'binding-value'), outputBinding('clicked', bindingClickHandler)],
49+
inputs: {
50+
...aliasedInput('greet', 'traditional-greeting'), // This will be ignored due to bindings
51+
},
52+
on: {
53+
clicked: clickHandler, // This should still work alongside bindings
54+
},
55+
});
56+
57+
// Only binding should work for inputs
58+
expect(screen.getByTestId('value')).toHaveTextContent('binding-value');
59+
expect(screen.getByTestId('greeting')).toHaveTextContent('hello'); // Default value, not traditional
60+
61+
const button = screen.getByTestId('emit-button');
62+
button.click();
63+
64+
// Both binding and traditional handlers should be called for outputs
65+
expect(bindingClickHandler).toHaveBeenCalledWith('clicked: binding-value');
66+
expect(clickHandler).toHaveBeenCalledWith('clicked: binding-value');
67+
68+
// Should show warning about mixed usage for inputs
69+
expect(consoleSpy).toHaveBeenCalledWith(
70+
'ATL: You specified both bindings and traditional inputs. ' +
71+
'Angular does not allow mixing setInput() with inputBinding(). ' +
72+
'Only bindings will be used for inputs. Use bindings for all inputs to avoid this warning.',
73+
);
74+
75+
expect(consoleSpy).toHaveBeenCalledWith(
76+
'ATL: You specified both bindings and traditional output listeners. ' +
77+
'Consider using outputBinding() for all outputs for consistency.',
78+
);
79+
80+
consoleSpy.mockRestore();
81+
});
4182
});

0 commit comments

Comments
 (0)