Skip to content

Commit

Permalink
fix(eslint-plugin): fix no-dispatch-in-effects rule to detect inject (n…
Browse files Browse the repository at this point in the history
  • Loading branch information
va-stefanek committed Jul 16, 2023
1 parent 8fd280f commit d2cc042
Showing 1 changed file with 316 additions and 11 deletions.
327 changes: 316 additions & 11 deletions modules/eslint-plugin/spec/rules/no-dispatch-in-effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,83 @@ type MessageIds = ESLintUtils.InferMessageIdsTypeFromRule<typeof rule>;
type Options = ESLintUtils.InferOptionsTypeFromRule<typeof rule>;
type RunTests = TSESLint.RunTests<MessageIds, Options>;

const valid: () => RunTests['valid'] = () => [
const validConstructor: () => RunTests['valid'] = () => [
`
import { Store } from '@ngrx/store'
class Ok {
readonly effect = somethingOutside();
}`,
`
import { Store } from '@ngrx/store'
class Ok1 {
effect = createEffect(() => this.actions.pipe(
ofType('PING'),
tap(() => ({ type: 'PONG' }))
))
constructor(private actions: Actions, private store: Store) {}
}`,
`
import { Store } from '@ngrx/store'
class Ok2 {
readonly effect: CreateEffectMetadata
constructor(private actions: Actions, private store$: Store) {
this.effect = createEffect(
() => ({ scheduler = asyncScheduler } = {}) =>
this.actions.pipe(
ofType(customerActions.remove),
tap(() => {
customObject.dispatch({ somethingElse: true })
return customerActions.removeSuccess()
}),
),
{ dispatch: false },
)
}
}`,
];

const validInject: () => RunTests['valid'] = () => [
`
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class Ok3 {
private readonly actions = inject(Actions);
private readonly store = inject(Store);
class Ok {
readonly effect = somethingOutside();
effect = createEffect(() => this.actions.pipe(
ofType('PING'),
tap(() => ({ type: 'PONG' }))
))
}`,
`
import { Store } from '@ngrx/store'
import { inject } from '@angular/inject'
class Ok5 {
private readonly actions = inject(Actions);
private readonly store = inject(Store);
class Ok1 {
effect = createEffect(() => this.actions.pipe(
ofType('PING'),
tap(() => ({ type: 'PONG' }))
))
constructor(private actions: Actions, private store: Store) {}
}`,
`
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class Ok2 {
class Ok5 {
readonly effect: CreateEffectMetadata
private readonly actions = inject(Actions);
private readonly store$ = inject(Store);
constructor(private actions: Actions, private store$: Store) {
constructor() {
this.effect = createEffect(
() => ({ scheduler = asyncScheduler } = {}) =>
this.actions.pipe(
Expand All @@ -54,7 +106,7 @@ class Ok2 {
}`,
];

const invalid: () => RunTests['invalid'] = () => [
const invalidConstructor: () => RunTests['invalid'] = () => [
fromFixture(
`
import { Store } from '@ngrx/store'
Expand Down Expand Up @@ -287,7 +339,260 @@ class NotOk3 {
),
];

const invalidInject: () => RunTests['invalid'] = () => [
fromFixture(
`
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class NotOk4 {
private actions = inject(Actions);
private store = inject(Store);
effect = createEffect(
() => {
return this.actions.pipe(
ofType(someAction),
tap(() => this.store.dispatch(awesomeAction())),
~~~~~~~~~~~~~~~~~~~ [${noDispatchInEffects} suggest]
)
},
{ dispatch: false },
)
}`,
{
suggestions: [
{
messageId: noDispatchInEffectsSuggest,
output: `
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class NotOk4 {
private actions = inject(Actions);
private store = inject(Store);
effect = createEffect(
() => {
return this.actions.pipe(
ofType(someAction),
tap(() => (awesomeAction())),
)
},
{ dispatch: false },
)
}`,
},
],
}
),
fromFixture(
`
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class NotOk5 {
private readonly actions = inject(Actions);
private readonly store = inject(Store);
readonly effect = createEffect(() => condition ? this.actions.pipe(
ofType(userActions.add),
tap(() => {
return this.store.dispatch(userActions.addSuccess)
~~~~~~~~~~~~~~~~~~~ [${noDispatchInEffects} suggest]
})
) : this.actions.pipe())
}`,
{
suggestions: [
{
messageId: noDispatchInEffectsSuggest,
output: `
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class NotOk5 {
private readonly actions = inject(Actions);
private readonly store = inject(Store);
readonly effect = createEffect(() => condition ? this.actions.pipe(
ofType(userActions.add),
tap(() => {
return (userActions.addSuccess)
})
) : this.actions.pipe())
}`,
},
],
}
),
fromFixture(
`
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class NotOk6 {
private readonly actions = inject(Actions);
private readonly customName = inject(Store);
effect = createEffect(
() => ({ debounce = 200 } = {}) =>
this.actions.pipe(
ofType(actions.ping),
tap(() => {
return this.customName.dispatch(/* you shouldn't do this */ actions.pong())
~~~~~~~~~~~~~~~~~~~~~~~~ [${noDispatchInEffects} suggest]
}),
),
)
}`,
{
suggestions: [
{
messageId: noDispatchInEffectsSuggest,
output: `
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class NotOk6 {
private readonly actions = inject(Actions);
private readonly customName = inject(Store);
effect = createEffect(
() => ({ debounce = 200 } = {}) =>
this.actions.pipe(
ofType(actions.ping),
tap(() => {
return (/* you shouldn't do this */ actions.pong())
}),
),
)
}`,
},
],
}
),
fromFixture(
`
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class NotOk7 {
readonly effect : CreateEffectMetadata
readonly effect : CreateEffectMetadata
private readonly actions = inject(Actions);
private readonly store = inject(Store);
private readonly store$ = inject(Store);
constructor() {
this.effect = createEffect(
() =>
this.actions.pipe(
ofType(bookActions.load),
map(() => {
this.store$.dispatch(bookActions.loadSuccess());// you shouldn't do this
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [${noDispatchInEffects} suggest 0]
return somethingElse()
}),
),
{ dispatch: true, useEffectsErrorHandler: false, ...options },
)
this.effect = createEffect(
() =>
this.actions.pipe(
ofType(bookActions.load),
tap(() => store.dispatch(bookActions.loadSuccess()))
~~~~~~~~~~~~~~ [${noDispatchInEffects} suggest 1]
),
)
}
ngOnDestroy() {
store.dispatch()
}
}`,
{
suggestions: [
{
messageId: noDispatchInEffectsSuggest,
output: `
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class NotOk7 {
readonly effect : CreateEffectMetadata
readonly effect : CreateEffectMetadata
private readonly actions = inject(Actions);
private readonly store = inject(Store);
private readonly store$ = inject(Store);
constructor() {
this.effect = createEffect(
() =>
this.actions.pipe(
ofType(bookActions.load),
map(() => {
;// you shouldn't do this
return somethingElse()
}),
),
{ dispatch: true, useEffectsErrorHandler: false, ...options },
)
this.effect = createEffect(
() =>
this.actions.pipe(
ofType(bookActions.load),
tap(() => store.dispatch(bookActions.loadSuccess()))
),
)
}
ngOnDestroy() {
store.dispatch()
}
}`,
},
{
messageId: noDispatchInEffectsSuggest,
output: `
import { Store } from '@ngrx/store'
import { inject } from '@angular/core'
class NotOk7 {
readonly effect : CreateEffectMetadata
readonly effect : CreateEffectMetadata
private readonly actions = inject(Actions);
private readonly store = inject(Store);
private readonly store$ = inject(Store);
constructor() {
this.effect = createEffect(
() =>
this.actions.pipe(
ofType(bookActions.load),
map(() => {
this.store$.dispatch(bookActions.loadSuccess());// you shouldn't do this
return somethingElse()
}),
),
{ dispatch: true, useEffectsErrorHandler: false, ...options },
)
this.effect = createEffect(
() =>
this.actions.pipe(
ofType(bookActions.load),
tap(() => (bookActions.loadSuccess()))
),
)
}
ngOnDestroy() {
store.dispatch()
}
}`,
},
],
}
),
];

ruleTester().run(path.parse(__filename).name, rule, {
valid: valid(),
invalid: invalid(),
valid: [...validConstructor(), ...validInject()],
invalid: [...invalidConstructor(), ...invalidInject()],
});

0 comments on commit d2cc042

Please sign in to comment.