operations-live-refresh.service.spec.ts 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import { fakeAsync, flushMicrotasks, TestBed, tick } from '@angular/core/testing';
  2. import { Subject } from 'rxjs';
  3. import { NavigationEnd, Router } from '@angular/router';
  4. import { OperationsService } from './operations.service';
  5. import { OperationsLiveRefreshService } from './operations-live-refresh.service';
  6. import { ADMIN_SESSION_KEY } from './admin.constants';
  7. describe('OperationsLiveRefreshService', () => {
  8. let changes: Subject<'local' | 'broadcast'>;
  9. let request: jasmine.Spy;
  10. let routerEvents: Subject<NavigationEnd>;
  11. let router: { url: string; events: Subject<NavigationEnd>; getCurrentNavigation: () => null; parseUrl: (url: string) => { toString: () => string } };
  12. beforeEach(() => {
  13. changes = new Subject();
  14. request = jasmine.createSpy('request');
  15. routerEvents = new Subject<NavigationEnd>();
  16. router = { url: '/admin/members', events: routerEvents, getCurrentNavigation: () => null, parseUrl: (url) => ({ toString: () => url }) };
  17. sessionStorage.setItem(ADMIN_SESSION_KEY, JSON.stringify({ sessionToken: 'test-session' }));
  18. TestBed.configureTestingModule({
  19. providers: [
  20. OperationsLiveRefreshService,
  21. { provide: OperationsService, useValue: { request, dataChanges$: changes } },
  22. { provide: Router, useValue: router },
  23. ],
  24. });
  25. });
  26. afterEach(() => sessionStorage.removeItem(ADMIN_SESSION_KEY));
  27. it('checks on entry without polling or reloading the page afterward', fakeAsync(() => {
  28. request.and.returnValue(Promise.resolve(version('v1')));
  29. const refresh = jasmine.createSpy('refresh');
  30. const stop = TestBed.inject(OperationsLiveRefreshService).watch(['learning'], refresh);
  31. flushMicrotasks();
  32. expect(refresh).not.toHaveBeenCalled();
  33. tick(15_000);
  34. flushMicrotasks();
  35. expect(request).toHaveBeenCalledTimes(1);
  36. expect(refresh).not.toHaveBeenCalled();
  37. stop();
  38. }));
  39. it('reloads immediately after local and cross-tab writes', () => {
  40. request.and.returnValue(Promise.resolve(version('v1')));
  41. const refresh = jasmine.createSpy('refresh');
  42. const stop = TestBed.inject(OperationsLiveRefreshService).watch(['appointments'], refresh);
  43. changes.next('local');
  44. changes.next('broadcast');
  45. expect(refresh).toHaveBeenCalledTimes(2);
  46. stop();
  47. });
  48. it('stops checking after logout', fakeAsync(() => {
  49. request.and.returnValue(Promise.resolve(version('v1')));
  50. const stop = TestBed.inject(OperationsLiveRefreshService).watch(['learning'], () => undefined);
  51. flushMicrotasks();
  52. expect(request).toHaveBeenCalledTimes(1);
  53. sessionStorage.removeItem(ADMIN_SESSION_KEY);
  54. tick(10_000);
  55. flushMicrotasks();
  56. expect(request).toHaveBeenCalledTimes(1);
  57. stop();
  58. }));
  59. it('does not check again when a detached page is reattached', fakeAsync(() => {
  60. request.and.returnValues(Promise.resolve(version('v1')), Promise.resolve(version('v2')));
  61. const refresh = jasmine.createSpy('refresh');
  62. const stop = TestBed.inject(OperationsLiveRefreshService).watch(['learning'], refresh);
  63. flushMicrotasks();
  64. router.url = '/admin/schedule';
  65. routerEvents.next(new NavigationEnd(1, '/admin/members', '/admin/schedule'));
  66. tick(10_000);
  67. flushMicrotasks();
  68. expect(request).toHaveBeenCalledTimes(1);
  69. changes.next('local');
  70. expect(refresh).not.toHaveBeenCalled();
  71. router.url = '/admin/members';
  72. routerEvents.next(new NavigationEnd(2, '/admin/schedule', '/admin/members'));
  73. flushMicrotasks();
  74. expect(request).toHaveBeenCalledTimes(1);
  75. expect(refresh).not.toHaveBeenCalled();
  76. stop();
  77. }));
  78. });
  79. function version(value: string) {
  80. return { version: value, scopeVersions: { learning: value }, refreshedAt: '2026-09-25T00:00:00.000Z', syncStatus: { state: 'current' as const } };
  81. }