Change detection not work in Angular unit test

Discussão em 'Angular' iniciado por Martin Araujo, Março 27, 2025.

  1. My test does not pass. If I remove the requiredPermission structural directive, the test pass.

    • report-button implementation is just a boolean input signal that check a button.

    When I removed the directive my component react to the changes with fixture.detectChanges(), but when I use it in the template, after the first fixture.detectChanges() nothing inside the ng-container react to changes.

    Im using change detection OnPush in my components.

    <div>
    <ng-container *requiredPermission="accessControl.PERFORMANCE_REPORT_PERMISSION.VIEW">
    <report-button [isChecked]="reportPageStore.reportType() === 'performance'"
    data-testid="performanceButton" />
    </ng-container>
    <ng-container *requiredPermission="accessControl.FUEL_COST_REPORT_PERMISSION.VIEW">
    <report-button [isChecked]="reportPageStore.reportType() === 'fuel-and-cost'"
    data-testid="fuelAndCostButton"/>
    </ng-container>
    </div>


    const reportPageStoreMock = {
    reportType: signal<MachineReportType | undefined>(undefined),
    isReportFormComplete: signal<boolean>(false),
    openSidebar: jest.fn(),
    setReportType: jest.fn()
    };

    const userStoreMock = {
    userState: signal<IUserState>({
    name: 'test',
    email: 'test',
    imageUrl: 'url',
    accessToken: 'token',
    companies: [],
    hasBackofficeProfile: false,
    profiles: [],
    actualRole: null,
    profileSelectedPermissions: [],
    profileSelected: ProfileType.BACKOFFICE,
    }),
    profileSelectedPermissions: signal<string[]>([
    PERFORMANCE_REPORT_PERMISSION.VIEW,
    FUEL_COST_REPORT_PERMISSION.VIEW
    ]),
    };

    describe('ReportTypesComponent', () => {
    let component: ReportTypesComponent;
    let fixture: ComponentFixture<ReportTypesComponent>;

    beforeEach(async () => {
    await TestBed.configureTestingModule({
    imports: [
    ReportTypesComponent,
    ReportButtonComponent,
    PermissionDirective
    ],
    providers: [
    RouterModule,
    { provide: UserStoreService, useValue: userStoreMock },
    { provide: ReportContainerStoreService, useValue: reportPageStoreMock },
    ]
    }).compileComponents();

    fixture = TestBed.createComponent(ReportTypesComponent);

    fixture.detectChanges();
    });

    it('ReportTypesComponent: should set isChecked to the correct report-button', async () => {
    const performanceButton = fixture.debugElement.query(
    By.css('[data-testid="performanceButton"]')
    );
    const fuelAndCostButton = fixture.debugElement.query(
    By.css('[data-testid="fuelAndCostButton"]')
    );
    fixture.detectChanges();

    expect(performanceButton.componentInstance.isChecked()).toBe(false);
    expect(fuelAndCostButton.componentInstance.isChecked()).toBe(false);

    reportPageStoreMock.reportType.set('performance');
    fixture.detectChanges();

    // this is the line that fail

    expect(performanceButton.componentInstance.isChecked()).toBe(true);
    });


    export class PermissionDirective {
    readonly #userStore = inject(UserStoreService);
    readonly #viewContainerRef = inject(ViewContainerRef);
    readonly #templateRef = inject(TemplateRef);

    public requiredPermission = input<string>();
    public somePermission = input<string[]>();

    public hasPermissionsEffect = effect(() => {
    const requiredPermission = this.requiredPermission();
    if (!requiredPermission) {
    return;
    }

    const permissions = this.#userStore.profileSelectedPermissions();
    if (requiredPermission && permissions.includes(requiredPermission)) {
    this.addTemplate();
    } else {
    this.clearTemplate();
    }
    });

    private addTemplate() {
    this.#viewContainerRef.clear();
    this.#viewContainerRef.createEmbeddedView(this.#templateRef);
    }

    private clearTemplate() {
    this.#viewContainerRef.clear();
    }
    }


    The test does not pass

    Continue reading...

Compartilhe esta Página