@angular/core/testing#flushMicrotasks TypeScript Examples

The following examples show how to use @angular/core/testing#flushMicrotasks. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: leaderboard.service.spec.ts    From one-platform with MIT License 6 votes vote down vote up
describe('LeaderboardService', () => {
  let service: LeaderboardService;
  let controller: ApolloTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ApolloTestingModule],
    });
    service = TestBed.inject(LeaderboardService);
    controller = TestBed.inject(ApolloTestingController);
  });

  afterEach(() => {
    controller.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('expect and answer', fakeAsync(() => {
    service
      .listLHLeaderboard(LeaderboardCategory.ACCESSIBILITY)
      .subscribe((leaderboard) => {
        const data = leaderboard.data.listLHLeaderboard;
        expect(data.count).toEqual(1);
      });
    flushMicrotasks();
    const op = controller.expectOne(ListLHLeaderboard);
    expect(op.operation.variables.type).toEqual(
      LeaderboardCategory.ACCESSIBILITY
    );

    op.flush({ data: { listLHLeaderboard: mockLeaderboardData } });

    controller.verify();
  }));
});
Example #2
Source File: angular-context.ts    From s-libs with MIT License 6 votes vote down vote up
/**
   * Advance time and trigger change detection. It is common to call this with no arguments to trigger change detection without advancing time.
   *
   * @param unit The unit of time `amount` represents. Accepts anything described in `@s-libs/s-core`'s [TimeUnit]{@linkcode https://simontonsoftware.github.io/s-js-utils/typedoc/enums/timeunit.html} enum.
   */
  tick(amount = 0, unit = 'ms'): void {
    if (
      isUndefined((window as any).Zone.current.get('FakeAsyncTestZoneSpec'))
    ) {
      throw new Error(
        '.tick() only works inside the .run() callback (because it needs to be in a fakeAsync zone)',
      );
    }

    // To simulate real life, trigger change detection before processing macro tasks. To further simulate real life, wait until the micro task queue is empty.
    flushMicrotasks();
    this.runChangeDetection();

    tick(convertTime(amount, unit, 'ms'));
    this.runChangeDetection();
  }
Example #3
Source File: delay-on-microtask-queue.spec.ts    From s-libs with MIT License 6 votes vote down vote up
describe('delayOnMicrotaskQueue()', () => {
  it('passes along values asynchronously', fakeAsync(() => {
    const source = new Subject<number>();
    const spy = jasmine.createSpy();
    source.pipe(delayOnMicrotaskQueue()).subscribe(spy);

    source.next(1);
    expect(spy).not.toHaveBeenCalled();
    flushMicrotasks();
    expectSingleCallAndReset(spy, 1);

    source.next(2);
    expect(spy).not.toHaveBeenCalled();
    flushMicrotasks();
    expectSingleCallAndReset(spy, 2);
  }));

  it(
    'passes along unsubscribes synchronously',
    testUnsubscribePropagation(delayOnMicrotaskQueue),
  );

  it(
    'passes along errors synchronously',
    testErrorPropagation(delayOnMicrotaskQueue),
  );

  it(
    'passes along completion synchronously',
    testCompletionPropagation(delayOnMicrotaskQueue),
  );
});
Example #4
Source File: playground.service.spec.ts    From one-platform with MIT License 5 votes vote down vote up
describe('PlaygroundService', () => {
  let service: PlaygroundService;
  let controller: ApolloTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ApolloTestingModule],
    });
    service = TestBed.inject(PlaygroundService);
    controller = TestBed.inject(ApolloTestingController);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('AuditWebsite mutation', fakeAsync(() => {
    service
      .auditWebsite(mockAuditWebsiteInput.property)
      .subscribe((auditId) => {
        expect(auditId).toEqual(mockAuditWebsite.data);
      });
    flushMicrotasks();
    const op = controller.expectOne((operation) => {
      expect(operation.query.definitions).toEqual(AuditWebsite.definitions);
      return true;
    });
    expect(op.operation.variables.property).toEqual(
      mockAuditWebsiteInput.property
    );

    op.flush({ data: mockAuditWebsite.data });

    controller.verify();
  } ) );

  it('Autorun subscription', fakeAsync(() => {
    service
      .autorun()
      .subscribe((doc) => {
        expect(doc).toEqual(mockAutorun.data.autorun);
      });
    flushMicrotasks();
    const op = controller.expectOne((operation) => {
      expect(operation.query.definitions).toEqual(Autorun.definitions);
      return true;
    });

    op.flush({ data: mockAutorun.data });

    controller.verify();
  }));
});
Example #5
Source File: dashboard.service.spec.ts    From one-platform with MIT License 4 votes vote down vote up
describe('DashboardService', () => {
  let service: DashboardService;
  let controller: ApolloTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ApolloTestingModule],
    });
    service = TestBed.inject(DashboardService);
    controller = TestBed.inject(ApolloTestingController);
  });

  afterEach(() => {
    controller.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('listLHProjects query', fakeAsync(() => {
    service.listLHProjects().subscribe((project) => {
      const data = project.data;
      expect(data).toEqual(mockListProjects as any);
    });
    flushMicrotasks();
    const op = controller.expectOne(ListLHProjects);
    expect(op.operation.variables.limit).toEqual(10000);

    op.flush({ data: mockListProjects });

    controller.verify();
  }));

  it('listLHProjectBranches query', fakeAsync(() => {
    service
      .listLHProjectBranches('eef123')
      .valueChanges.subscribe((project) => {
        const data = project.data;
        expect(data).toEqual(mockListProjectBranches);
      });
    flushMicrotasks();
    const op = controller.expectOne(ListLHProjectBranches);
    expect(op.operation.variables.projectId).toEqual('eef123');

    op.flush({ data: mockListProjectBranches });

    controller.verify();
  }));

  it('ListLHProjectScores query', fakeAsync(() => {
    service
      .ListLHProjectScores('eef123', 'one-platform')
      .subscribe((project) => {
        const data = project.data;
        expect(data).toEqual(mockListProjectScores as any);
      });
    flushMicrotasks();
    const op = controller.expectOne(ListLHProjectScores);
    expect(op.operation.variables.projectId).toEqual('eef123');
    expect(op.operation.variables.branch).toEqual('one-platform');

    op.flush({ data: mockListProjectScores });

    controller.verify();
  }));

  it('ListLHLeaderboard query', fakeAsync(() => {
    service
      .listLHLeaderboard(LeaderboardCategory.OVERALL, 'main', 'one-platform')
      .subscribe((project) => {
        const data = project.data;
        expect(data).toEqual(mockLeaderboardAPI as any);
      });
    flushMicrotasks();
    const op = controller.expectOne(ListLHLeaderboard);
    expect(op.operation.variables.type).toEqual(LeaderboardCategory.OVERALL);
    expect(op.operation.variables.projectId).toEqual('one-platform');
    expect(op.operation.variables.branch).toEqual('main');

    op.flush({ data: mockLeaderboardAPI });

    controller.verify();
  }));
});
Example #6
Source File: nas-model.directive.spec.ts    From s-libs with MIT License 4 votes vote down vote up
// `nasModel` is tested pretty thoroughly above, by the tests adapted from angular's suite. Here we hit a few more cases to complete code coverage.
describe('NasModelDirective', () => {
  it('can bind to different store objects over time', fakeAsync(() => {
    const store = initTest(MenuComponent, MenuStore, {
      template: `<input [nasModel]="textStore">`,
    });
    store.assign({ food: 'chicken', drink: 'coke' });
    const input = query('input');

    fixture.componentInstance.textStore = store('food');
    detectChanges();
    expect(input.value).toEqual('chicken');
    input.value = 'pork';
    dispatchEvent(input, 'input');

    fixture.componentInstance.textStore = store('drink');
    detectChanges();
    expect(input.value).toEqual('coke');

    fixture.componentInstance.textStore = store('food');
    detectChanges();
    expect(input.value).toEqual('pork');
  }));

  it('can control disabledness', fakeAsync(() => {
    const store = initSingleValueTest(`
      <input
        type="number"
        [nasModel]="store('num')"
        [disabled]="store('disabled').$ | async"
      />
    `);
    store.set({ num: 2, disabled: true });
    const input = query('input');

    detectChanges();
    flushMicrotasks();
    expect(input.disabled).toBe(true);

    store.assign({ disabled: false });
    detectChanges();
    flushMicrotasks();
    expect(input.disabled).toBe(false);
  }));

  it('handles `null` for the store (for async pipe compatibility)', () => {
    @Component({ template: `<input [nasModel]="store$ | async" />` })
    class StoreStreamComponent {
      store$ = new Subject<Store<string>>();
    }

    const ctx = new ComponentContext(StoreStreamComponent, {
      imports: [NasModelModule],
    });
    expect(() => {
      ctx.run(() => {
        const input: HTMLInputElement = ctx.fixture.debugElement.query(
          By.css('input'),
        ).nativeElement;
        setValue(input, 'updated value');
      });
    }).not.toThrowError();
  });

  it('handles `null` for disabled (for async pipe compatibility)', () => {
    @Component({
      template: `<input
        [nasModel]="store('val')"
        [disabled]="disabled$ | async"
      />`,
    })
    class StoreStreamComponent {
      store = new RootStore({ val: 'hi' });
      disabled$ = new Subject<boolean>();
    }

    const ctx = new ComponentContext(StoreStreamComponent, {
      imports: [NasModelModule],
    });
    expect(() => {
      ctx.run(() => {
        const input: HTMLInputElement = ctx.fixture.debugElement.query(
          By.css('input'),
        ).nativeElement;
        expect(input.disabled).toBe(false);

        ctx.getComponentInstance().disabled$.next(true);
        ctx.tick();
        expect(input.disabled).toBe(true);
      });
    }).not.toThrowError();
  });
});
Example #7
Source File: wrapped-control-superclass.spec.ts    From s-libs with MIT License 4 votes vote down vote up
describe('WrappedControlSuperclass tests using an old style fixture', () => {
  @Component({
    template: `
      <sl-string-component
        [(ngModel)]="string"
        (ngModelChange)="emissions = emissions + 1"
        #stringControl="ngModel"
        [disabled]="shouldDisable"
      ></sl-string-component>
      <div *ngIf="stringControl.touched">Touched!</div>
      <button (click)="shouldDisable = !shouldDisable">Toggle Disabled</button>
      <hr />
      <sl-date-component [(ngModel)]="date"></sl-date-component>
    `,
  })
  class TestComponent {
    emissions = 0;
    string = '';
    date = new Date();
    shouldDisable = false;
  }

  @Component({
    selector: `sl-string-component`,
    template: ` <input [formControl]="control" /> `,
    providers: [provideValueAccessor(StringComponent)],
    changeDetection: ChangeDetectionStrategy.OnPush,
  })
  class StringComponent extends WrappedControlSuperclass<string> {
    control = new FormControl();
  }

  @Component({
    selector: `sl-date-component`,
    template: ` <input type="datetime-local" [formControl]="control" /> `,
    providers: [provideValueAccessor(DateComponent)],
    changeDetection: ChangeDetectionStrategy.OnPush,
  })
  class DateComponent extends WrappedControlSuperclass<Date, string> {
    control = new FormControl();

    protected override innerToOuter(value: string): Date {
      return new Date(value + 'Z');
    }

    protected override outerToInner(value: Date): string {
      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- happens during initialization
      if (value === null) {
        return '';
      }
      return value.toISOString().substr(0, 16);
    }
  }

  class TestComponentContext extends ComponentContext<TestComponent> {
    constructor() {
      super(TestComponent, {
        imports: [FormsModule, ReactiveFormsModule],
        declarations: [DateComponent, StringComponent],
        // TODO: this can go away with component harnesses eventually
        providers: [{ provide: ComponentFixtureAutoDetect, useValue: true }],
      });
    }
  }

  let masterCtx: TestComponentContext;
  beforeEach(() => {
    masterCtx = new TestComponentContext();
  });

  function stringInput(): HTMLInputElement {
    return find<HTMLInputElement>(
      masterCtx.fixture,
      'sl-string-component input',
    );
  }

  function dateInput(): HTMLInputElement {
    return find<HTMLInputElement>(masterCtx.fixture, 'sl-date-component input');
  }

  function toggleDisabledButton(): HTMLButtonElement {
    return findButton(masterCtx.fixture, 'Toggle Disabled');
  }

  it('provides help for 2-way binding', () => {
    masterCtx.run(() => {
      masterCtx.getComponentInstance().string = 'initial value';
      masterCtx.tick();
      expect(stringInput().value).toBe('initial value');

      setValue(stringInput(), 'edited value');
      expect(masterCtx.getComponentInstance().string).toBe('edited value');
    });
  });

  it('can translate between inner and outer values', () => {
    masterCtx.run(() => {
      masterCtx.getComponentInstance().date = new Date('2018-09-03T21:00Z');
      masterCtx.tick();
      expect(dateInput().value).toBe('2018-09-03T21:00');

      setValue(dateInput(), '1980-11-04T10:00');
      expect(masterCtx.getComponentInstance().date).toEqual(
        new Date('1980-11-04T10:00Z'),
      );
    });
  });

  it('provides help for `onTouched`', () => {
    masterCtx.run(() => {
      expect(masterCtx.fixture.nativeElement.innerText).not.toContain(
        'Touched!',
      );
      stringInput().dispatchEvent(new Event('blur'));
      expect(masterCtx.fixture.nativeElement.innerText).toContain('Touched!');
    });
  });

  it('provides help for `[disabled]`', () => {
    masterCtx.run(() => {
      masterCtx.getComponentInstance().shouldDisable = true;
      masterCtx.tick();
      expect(stringInput().disabled).toBe(true);

      click(toggleDisabledButton());
      expect(stringInput().disabled).toBe(false);

      click(toggleDisabledButton());
      expect(stringInput().disabled).toBe(true);
    });
  });

  it('does not emit after an incoming change', () => {
    masterCtx.run(() => {
      expect(masterCtx.getComponentInstance().emissions).toBe(0);

      setValue(stringInput(), 'changed from within');
      expect(masterCtx.getComponentInstance().emissions).toBe(1);

      masterCtx.getComponentInstance().string = 'changed from without';
      masterCtx.fixture.detectChanges();
      flushMicrotasks();
      expect(masterCtx.getComponentInstance().emissions).toBe(1);

      click(toggleDisabledButton());
      click(toggleDisabledButton());
      expect(masterCtx.getComponentInstance().emissions).toBe(1);
    });
  });

  it('has the right class hierarchy', () => {
    masterCtx.run(() => {
      const component = masterCtx.fixture.debugElement.query(
        By.directive(StringComponent),
      ).componentInstance;
      expect(component instanceof InjectableSuperclass).toBe(true);
      expect(component instanceof DirectiveSuperclass).toBe(true);
      expect(component instanceof FormComponentSuperclass).toBe(true);
    });
  });
});
Example #8
Source File: test-call.spec.ts    From s-libs with MIT License 4 votes vote down vote up
describe('TestCall', () => {
  @Component({ template: 'Hello, {{name}}!' })
  class TestComponent {
    @Input() name!: string;
  }

  describe('.callInfo', () => {
    it('is populated with a jasmine.CallInfo object', () => {
      const controller = new AsyncMethodController(
        navigator.clipboard,
        'readText',
      );
      navigator.clipboard.readText();

      const { callInfo } = controller.expectOne([]);

      // We'd love to test that this is just an "instanceof jasmine.CallInfo", but that's not really a thing. So we'll approximate it by ensuring a couple properties exist.
      expect(callInfo.returnValue).toBeInstanceOf(Promise);
      expect(callInfo.object).toBe(navigator.clipboard);
    });
  });

  describe('.flush()', () => {
    it('causes the call to be fulfilled with the given value', fakeAsync(() => {
      const controller = new AsyncMethodController(
        navigator.clipboard,
        'readText',
      );
      const spy = jasmine.createSpy();
      navigator.clipboard.readText().then(spy);
      const testCall = controller.match(() => true)[0];

      testCall.flush('the clipboard text');
      flushMicrotasks();

      expectSingleCallAndReset(spy, 'the clipboard text');
    }));

    it('triggers a tick if appropriate', () => {
      const ctx = new ComponentContext(TestComponent);
      const controller = new AsyncMethodController(
        navigator.clipboard,
        'readText',
      );

      ctx.run(() => {
        navigator.clipboard.readText();
        const testCall = controller.expectOne([]);

        ctx.getComponentInstance().name = 'Changed Guy';
        testCall.flush('this is the clipboard content');
        expect(ctx.fixture.nativeElement.textContent).toContain('Changed Guy');
      });
    });
  });

  describe('.error()', () => {
    it('causes the call to be rejected with the given reason', fakeAsync(() => {
      const controller = new AsyncMethodController(
        navigator.clipboard,
        'readText',
      );
      const spy = jasmine.createSpy();
      navigator.clipboard.readText().catch(spy);
      const testCall = controller.match(() => true)[0];

      testCall.error('some problem');
      flushMicrotasks();

      expectSingleCallAndReset(spy, 'some problem');
    }));

    it('triggers a tick if appropriate', () => {
      const ctx = new ComponentContext(TestComponent);
      const controller = new AsyncMethodController(
        navigator.clipboard,
        'readText',
      );

      ctx.run(() => {
        navigator.clipboard.readText().catch(noop);
        const testCall = controller.expectOne([]);

        ctx.getComponentInstance().name = 'Changed Guy';
        testCall.error('permission denied');
        expect(ctx.fixture.nativeElement.textContent).toContain('Changed Guy');
      });
    });
  });

  describe('.maybeTick()', () => {
    it('does not call .tick() when autoTick is false', () => {
      const ctx = new ComponentContext(TestComponent);
      const controller = new AsyncMethodController(
        navigator.clipboard,
        'readText',
        { autoTick: false },
      );

      ctx.run(() => {
        navigator.clipboard.readText();
        ctx.getComponentInstance().name = 'Spy';
        controller.expectOne([]).flush('this is the clipboard content');

        expect(ctx.fixture.nativeElement.textContent).not.toContain('Spy');
      });
    });

    it('gracefully handles being run outside an AngularContext', () => {
      const controller = new AsyncMethodController(
        navigator.clipboard,
        'readText',
      );

      navigator.clipboard.readText();
      expect(() => {
        controller.expectOne([]).flush('this is the clipboard content');
      }).not.toThrowError();
    });
  });
});