1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

Combined State (#7383)

* Introduce Combined State

* Cleanup Test

* Update Fakes

* Address PR Feedback

* Update libs/common/src/platform/state/implementations/default-active-user-state.ts

Co-authored-by: Matt Gibson <mgibson@bitwarden.com>

* Prettier

* Get rid of ReplaySubject reference

---------

Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
This commit is contained in:
Justin Baur
2024-01-04 16:30:20 -05:00
committed by GitHub
parent 312197b8c7
commit 5e11cb212d
13 changed files with 280 additions and 339 deletions

View File

@@ -1,10 +1,10 @@
import { ReplaySubject, firstValueFrom, timeout } from "rxjs";
import { Observable, ReplaySubject, firstValueFrom, map, timeout } from "rxjs";
import { DerivedState, GlobalState, SingleUserState, ActiveUserState } from "../src/platform/state";
// eslint-disable-next-line import/no-restricted-paths -- using unexposed options for clean typing in test class
import { StateUpdateOptions } from "../src/platform/state/state-update-options";
// eslint-disable-next-line import/no-restricted-paths -- using unexposed options for clean typing in test class
import { UserState, activeMarker } from "../src/platform/state/user-state";
import { CombinedState, UserState, activeMarker } from "../src/platform/state/user-state";
import { UserId } from "../src/types/guid";
const DEFAULT_TEST_OPTIONS: StateUpdateOptions<any, any> = {
@@ -57,9 +57,19 @@ export class FakeGlobalState<T> implements GlobalState<T> {
}
}
export class FakeUserState<T> implements UserState<T> {
abstract class FakeUserState<T> implements UserState<T> {
// eslint-disable-next-line rxjs/no-exposed-subjects -- exposed for testing setup
stateSubject = new ReplaySubject<T>(1);
stateSubject = new ReplaySubject<CombinedState<T>>(1);
protected userId: UserId;
state$: Observable<T>;
combinedState$: Observable<CombinedState<T>>;
constructor() {
this.combinedState$ = this.stateSubject.asObservable();
this.state$ = this.combinedState$.pipe(map(([_userId, state]) => state));
}
update: <TCombine>(
configureState: (state: T, dependency: TCombine) => T,
@@ -75,34 +85,24 @@ export class FakeUserState<T> implements UserState<T> {
return current;
}
const newState = configureState(current, combinedDependencies);
this.stateSubject.next(newState);
this.stateSubject.next([this.userId, newState]);
return newState;
});
updateMock = this.update as jest.MockedFunction<typeof this.update>;
updateFor: <TCombine>(
userId: UserId,
configureState: (state: T, dependency: TCombine) => T,
options?: StateUpdateOptions<T, TCombine>,
) => Promise<T> = jest.fn();
getFromState: () => Promise<T> = jest.fn(async () => {
return await firstValueFrom(this.state$.pipe(timeout(10)));
});
get state$() {
return this.stateSubject.asObservable();
}
}
export class FakeSingleUserState<T> extends FakeUserState<T> implements SingleUserState<T> {
constructor(readonly userId: UserId) {
super();
this.userId = userId;
}
}
export class FakeActiveUserState<T> extends FakeUserState<T> implements ActiveUserState<T> {
[activeMarker]: true;
changeActiveUser(userId: UserId) {
this.userId = userId;
}
}
export class FakeDerivedState<T> implements DerivedState<T> {