Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(store): create action stream that shows the action lifecycle (#255
) * feat(store): create action stream that shows the action lifecycle * feat(store): update unit test to show that all cbs are called correctly * feat(store): update docs * feat(store): remove ActionContext generic arg * chore: misc cleanup * feat(store): add ofActionErrored Operator * feat(store): update changelog * feat(store): update changelog
- Loading branch information
Showing
13 changed files
with
505 additions
and
326 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,12 @@ | ||
import { Injectable, Optional, SkipSelf } from '@angular/core'; | ||
import { Injectable } from '@angular/core'; | ||
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; | ||
|
||
/** | ||
* BehaviorSubject of the entire state. | ||
*/ | ||
@Injectable() | ||
export class StateStream extends BehaviorSubject<any> { | ||
constructor( | ||
@Optional() | ||
@SkipSelf() | ||
parent: StateStream | ||
) { | ||
constructor() { | ||
super({}); | ||
|
||
if (parent) { | ||
Object.assign(this, parent); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,100 @@ | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { Action } from '../src/action'; | ||
import { State } from '../src/state'; | ||
import { META_KEY } from '../src/symbols'; | ||
import { timer } from 'rxjs/observable/timer'; | ||
import { TestBed } from '@angular/core/testing'; | ||
import { NgxsModule } from '../src/module'; | ||
import { Store } from '../src/store'; | ||
import { Actions } from '../src/actions-stream'; | ||
import { tap } from 'rxjs/operators'; | ||
import { ofActionComplete, ofActionDispatched, ofAction, ofActionErrored } from '../src/of-action'; | ||
import { _throw } from 'rxjs/observable/throw'; | ||
|
||
describe('Action', () => { | ||
it('supports multiple actions', () => { | ||
class Action1 { | ||
static type = 'ACTION 1'; | ||
} | ||
let store: Store; | ||
let actions: Actions; | ||
|
||
class Action2 { | ||
static type = 'ACTION 2'; | ||
} | ||
class Action1 { | ||
static type = 'ACTION 1'; | ||
} | ||
|
||
class Action2 { | ||
static type = 'ACTION 2'; | ||
} | ||
|
||
class ErrorAction { | ||
static type = 'ErrorAction'; | ||
} | ||
|
||
@State({ | ||
name: 'bar' | ||
}) | ||
class BarStore { | ||
@Action([Action1, Action2]) | ||
foo() {} | ||
|
||
@State({ | ||
name: 'bar' | ||
}) | ||
class BarStore { | ||
@Action([Action1, Action2]) | ||
foo() {} | ||
@Action(ErrorAction) | ||
onError() { | ||
return _throw(new Error('this is a test error')); | ||
} | ||
} | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [NgxsModule.forRoot([BarStore])] | ||
}); | ||
|
||
store = TestBed.get(Store); | ||
actions = TestBed.get(Actions); | ||
}); | ||
|
||
it('supports multiple actions', () => { | ||
const meta = BarStore[META_KEY]; | ||
|
||
expect(meta.actions[Action1.type]).toBeDefined(); | ||
expect(meta.actions[Action2.type]).toBeDefined(); | ||
}); | ||
}); | ||
|
||
describe('Actions', () => { | ||
it('basic', () => { | ||
let happened = false; | ||
it('calls actions on dispatch and on complete', () => { | ||
let callbackCalledCount = 0; | ||
|
||
class Action1 { | ||
static type = 'ACTION 1'; | ||
} | ||
|
||
@State({ name: 'Bar' }) | ||
class BarStore { | ||
@Action(Action1) | ||
bar() { | ||
return timer(10).pipe(tap(() => (happened = true))); | ||
} | ||
} | ||
actions.pipe(ofAction(Action1)).subscribe(action => { | ||
callbackCalledCount++; | ||
}); | ||
|
||
TestBed.configureTestingModule({ | ||
imports: [NgxsModule.forRoot([BarStore])] | ||
actions.pipe(ofActionDispatched(Action1)).subscribe(action => { | ||
callbackCalledCount++; | ||
}); | ||
|
||
const store = TestBed.get(Store); | ||
const actions = TestBed.get(Actions); | ||
actions.pipe(ofActionComplete(Action1)).subscribe(action => { | ||
callbackCalledCount++; | ||
|
||
actions.subscribe(action => { | ||
expect(happened).toBeFalsy(); | ||
expect(callbackCalledCount).toBe(4); | ||
}); | ||
|
||
store.dispatch(new Action1()); | ||
}); | ||
|
||
it('calls only the dispatched and error action', () => { | ||
let callbackCalledCount = 0; | ||
|
||
actions.pipe(ofAction(Action1)).subscribe(action => { | ||
callbackCalledCount++; | ||
}); | ||
|
||
actions.pipe(ofActionDispatched(ErrorAction)).subscribe(action => { | ||
callbackCalledCount++; | ||
}); | ||
|
||
actions.pipe(ofActionComplete(ErrorAction)).subscribe(action => { | ||
callbackCalledCount++; | ||
}); | ||
|
||
actions.pipe(ofActionErrored(ErrorAction)).subscribe(action => { | ||
callbackCalledCount++; | ||
|
||
expect(callbackCalledCount).toBe(2); | ||
}); | ||
|
||
store.dispatch(new ErrorAction()); | ||
}); | ||
}); |
Oops, something went wrong.