Repository: angular/angular
File: packages/forms/signals/test/web/form_field_directive.spec.ts (TypeScript)

Code before:
        const component = fixture.componentInstance;
        expect(component.customControl().hidden()).toBe(true);

        act(() => component.field.set(component.f.y));
        expect(component.customControl().hidden()).toBe(false);
      });

      it('should warn when a hidden field is rendered', () => {
        const warnSpy = spyOn(console, 'warn');
        @Component({
          imports: [FormField],
          template: `<input [formField]="f" />`,
        })
        class TestCmp {
          readonly f = form(signal(''), (p) => {
            hidden(p, () => true);
          });
        }

        act(() => TestBed.createComponent(TestCmp));
        expect(warnSpy).toHaveBeenCalledWith(
          jasmine.stringMatching(/Field '.*' is hidden but is being rendered/),
        );
      });

      it('should not warn when a hidden field is guarded by @if', () => {
        const warnSpy = spyOn(console, 'warn');
        @Component({
          imports: [FormField],
          template: `
            @if (!f().hidden()) {
              <input [formField]="f" />
            }
          `,
        })
        class TestCmp {
          readonly f = form(signal(''), (p) => {
            hidden(p, () => true);
          });
        }

        act(() => TestBed.createComponent(TestCmp));
        expect(warnSpy).not.toHaveBeenCalled();
      });
    });

    describe('invalid', () => {
      it('should bind to custom control', () => {
        @Component({
          selector: 'custom-control',
          template: '<input #i [value]="value()" (input)="value.set(i.value)" />',

Review comment (suggestion):
To make sure we have the timing/reactivity right, can we test toggling `hidden()` to make sure the `@if` destroys the view before it runs the effect containing the warning?

(Or is this obviously how it works for those who understand the effect / view lifecycle?)

Code after:
        const component = fixture.componentInstance;
        expect(component.customControl().hidden()).toBe(true);

        act(() => component.field.set(component.f.y));
        expect(component.customControl().hidden()).toBe(false);
      });

      it('should warn when a hidden field is rendered', () => {
        const warnSpy = spyOn(console, 'warn');
        @Component({
          imports: [FormField],
          template: `<input [formField]="f" />`,
        })
        class TestCmp {
          readonly f = form(signal(''), (p) => {
            hidden(p, () => true);
          });
        }

        act(() => TestBed.createComponent(TestCmp));
        expect(warnSpy).toHaveBeenCalledWith(
          jasmine.stringMatching(/Field '.*' is hidden but is being rendered/),
        );
      });

      it('should not warn when a hidden field is guarded by @if', () => {
        const isHidden = signal(false);
        const warnSpy = spyOn(console, 'warn');
        @Component({
          imports: [FormField],
          template: `
            @if (!f().hidden()) {
              <input [formField]="f" />
            }
          `,
        })
        class TestCmp {
          readonly f = form(signal(''), (p) => {
            hidden(p, isHidden);
          });
        }

        act(() => TestBed.createComponent(TestCmp));
        expect(warnSpy).not.toHaveBeenCalled();

        act(() => isHidden.set(true));
        expect(warnSpy).not.toHaveBeenCalled();

        act(() => isHidden.set(false));
        expect(warnSpy).not.toHaveBeenCalled();
      });