Browse Source

Fixed tests and performance improvements.

pull/592/head
Sebastian 5 years ago
parent
commit
81e5756197
  1. 17
      frontend/app/framework/angular/routers/router-2-state.spec.ts
  2. 6
      frontend/app/framework/angular/routers/router-2-state.ts
  3. 72
      frontend/app/framework/state.spec.ts
  4. 48
      frontend/app/framework/state.ts
  5. 8
      frontend/app/framework/utils/types.ts

17
frontend/app/framework/angular/routers/router-2-state.spec.ts

@ -307,13 +307,28 @@ describe('Router2State', () => {
routerQueryParams.next({
key1: 'hello',
key2: 'squidex',
key2: 'cms',
key3: '!'
});
expect(invoked).toEqual(2);
});
it('Should not sync again when no state as changed', () => {
routerQueryParams.next({
key1: 'hello',
key2: 'squidex'
});
routerQueryParams.next({
key1: 'hello',
key2: 'squidex',
key3: '!'
});
expect(invoked).toEqual(1);
});
it('Should reset other values when synced from route', () => {
state.next({ other: 123 });

6
frontend/app/framework/angular/routers/router-2-state.ts

@ -299,12 +299,12 @@ export class Router2StateMap<T extends object> implements StateSynchronizerMap<T
update[key] = this.state.snapshot[key];
}
this.state.resetState(update);
if (this.state.resetState(update)) {
for (const action of this.syncDone) {
action();
}
}
}
public keep(key: keyof T & string) {
this.keysToKeep.push(key);
@ -344,7 +344,7 @@ function cleanupParams(query: Params) {
if (query.hasOwnProperty(key)) {
const value = query[key];
if (Types.isNull(value) || Types.isUndefined(key)) {
if (Types.isNull(value) || Types.isUndefined(value)) {
delete query[key];
}
}

72
frontend/app/framework/state.spec.ts

@ -0,0 +1,72 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { State } from './state';
describe('State', () => {
let state: State<any>;
beforeEach(() => {
state = new State<any>({});
});
it('should update state with new value', () => {
let updateCount = 0;
state.changes.subscribe(() => {
updateCount++;
});
const updated = state.next({ value: 1 });
expect(updateCount).toEqual(2);
expect(updated).toBeTruthy();
});
it('should reset state with new value', () => {
let updateCount = 0;
state.changes.subscribe(() => {
updateCount++;
});
const updated = state.resetState({ value: 1 });
expect(updateCount).toEqual(2);
expect(updated).toBeTruthy();
});
it('should not update state when nothing changed', () => {
let updateCount = 0;
state.changes.subscribe(() => {
updateCount++;
});
state.next({ value: 1 });
const updated = state.next({ value: 1 });
expect(updateCount).toEqual(2);
expect(updated).toBeFalsy();
});
it('should not reset state when nothing changed', () => {
let updateCount = 0;
state.changes.subscribe(() => {
updateCount++;
});
state.resetState({ value: 1 });
const updated = state.resetState({ value: 1 });
expect(updateCount).toEqual(2);
expect(updated).toBeFalsy();
});
});

48
frontend/app/framework/state.ts

@ -164,7 +164,6 @@ export class ResultSet<T> {
export class State<T extends {}> {
private readonly state: BehaviorSubject<Readonly<T>>;
private readonly initialState: Readonly<T>;
public get changes(): Observable<Readonly<T>> {
return this.state;
@ -185,39 +184,54 @@ export class State<T extends {}> {
}
public projectFrom2<M, N, O>(lhs: Observable<M>, rhs: Observable<N>, project: (l: M, r: N) => O, compare?: (x: O, y: O) => boolean) {
return combineLatest(lhs, rhs, (x, y) => project(x, y)).pipe(
distinctUntilChanged(compare), shareReplay(1));
return combineLatest([lhs, rhs]).pipe(
map(([x, y]) => project(x, y)), distinctUntilChanged(compare), shareReplay(1));
}
constructor(state: Readonly<T>) {
this.initialState = state;
this.state = new BehaviorSubject(state);
constructor(
private readonly initialState: Readonly<T>
) {
this.state = new BehaviorSubject(initialState);
}
public resetState(update?: ((v: T) => Readonly<T>) | Partial<T>) {
let newState = this.initialState;
return this.updateState(this.initialState, update);
}
public next(update: ((v: T) => Readonly<T>) | Partial<T>) {
return this.updateState(this.state.value, update);
}
private updateState(state: T, update?: ((v: T) => Readonly<T>) | Partial<T>) {
let newState = state;
if (update) {
if (Types.isFunction(update)) {
newState = update(this.initialState);
newState = update(state);
} else {
newState = { ...this.initialState, ...update };
newState = { ...state, ...update };
}
}
this.state.next(newState);
}
let isChanged = false;
public next(update: ((v: T) => Readonly<T>) | Partial<T>) {
let newState: T;
const newKeys = Object.keys(newState);
if (Types.isFunction(update)) {
newState = update(this.state.value);
if (newKeys.length !== Object.keys(this.snapshot).length) {
isChanged = true;
} else {
newState = { ...this.state.value, ...update };
for (const key of newKeys) {
if (newState[key] !== this.snapshot[key]) {
isChanged = true;
break;
}
}
}
if (isChanged) {
this.state.next(newState);
}
return isChanged;
}
}

8
frontend/app/framework/utils/types.ts

@ -162,17 +162,17 @@ export module Types {
return true;
} else if (Types.isObject(lhs) && Types.isObject(rhs)) {
if (Object.keys(lhs).length !== Object.keys(rhs).length) {
const lhsKeys = Object.keys(lhs);
if (lhsKeys.length !== Object.keys(rhs).length) {
return false;
}
for (const key in lhs) {
if (lhs.hasOwnProperty(key)) {
for (const key of lhsKeys) {
if (!equals(lhs[key], rhs[key], lazyString)) {
return false;
}
}
}
return true;
}

Loading…
Cancel
Save