diff --git a/src/processors/LayoutProcessorsStore.test.ts b/src/processors/LayoutProcessorsStore.test.ts index dd451d0446..64ae47375a 100644 --- a/src/processors/LayoutProcessorsStore.test.ts +++ b/src/processors/LayoutProcessorsStore.test.ts @@ -27,4 +27,31 @@ describe('Layout processors Store', () => { remove(); expect(uut.getProcessors()).toEqual([]); }); + + it('should unregister only the subscribed processor', () => { + const firstProcessor = (value: any, _commandName: string) => value; + const secondProcessor = (value: any, _commandName: string) => value; + const thirdProcessor = (value: any, _commandName: string) => value; + uut.addProcessor(firstProcessor); + const { remove } = uut.addProcessor(secondProcessor); + uut.addProcessor(thirdProcessor); + + remove(); + + expect(uut.getProcessors()).toEqual([firstProcessor, thirdProcessor]); + }); + + it('should ignore repeated subscription removal', () => { + const processor = (value: any, _commandName: string) => value; + const firstSubscription = uut.addProcessor(processor); + const secondSubscription = uut.addProcessor(processor); + + firstSubscription.remove(); + firstSubscription.remove(); + + expect(uut.getProcessors()).toEqual([processor]); + + secondSubscription.remove(); + expect(uut.getProcessors()).toEqual([]); + }); }); diff --git a/src/processors/LayoutProcessorsStore.ts b/src/processors/LayoutProcessorsStore.ts index 96bce6e353..18509a925a 100644 --- a/src/processors/LayoutProcessorsStore.ts +++ b/src/processors/LayoutProcessorsStore.ts @@ -6,8 +6,16 @@ export class LayoutProcessorsStore { public addProcessor(processor: LayoutProcessor): ProcessorSubscription { this.layoutProcessors.push(processor); + let removed = false; - return { remove: () => this.removeProcessor(processor) }; + return { + remove: () => { + if (!removed) { + this.removeProcessor(processor); + removed = true; + } + }, + }; } public getProcessors(): LayoutProcessor[] { @@ -15,6 +23,7 @@ export class LayoutProcessorsStore { } private removeProcessor(processor: LayoutProcessor) { - this.layoutProcessors.splice(this.layoutProcessors.indexOf(processor)); + const index = this.layoutProcessors.indexOf(processor); + if (index !== -1) this.layoutProcessors.splice(index, 1); } } diff --git a/src/processors/OptionProcessorsStore.test.ts b/src/processors/OptionProcessorsStore.test.ts index 57f8769c30..ecff915be4 100644 --- a/src/processors/OptionProcessorsStore.test.ts +++ b/src/processors/OptionProcessorsStore.test.ts @@ -20,6 +20,15 @@ describe('Option processors Store', () => { expect(uut.getProcessors('topBar')).toEqual([processor, secondProcessor]); }); + it.each(['toString', '__proto__'])( + 'should register processors for the %s object path', + (optionPath) => { + const processor = (value: any, _commandName: string) => value; + uut.addProcessor(optionPath, processor); + expect(uut.getProcessors(optionPath)).toEqual([processor]); + } + ); + it('should unregister processor', () => { const processor = (value: any, _commandName: string) => value; const { remove } = uut.addProcessor('topBar', processor); @@ -27,4 +36,31 @@ describe('Option processors Store', () => { remove(); expect(uut.getProcessors('topBar')).toEqual([]); }); + + it('should unregister only the subscribed processor', () => { + const firstProcessor = (value: any, _commandName: string) => value; + const secondProcessor = (value: any, _commandName: string) => value; + const thirdProcessor = (value: any, _commandName: string) => value; + uut.addProcessor('topBar', firstProcessor); + const { remove } = uut.addProcessor('topBar', secondProcessor); + uut.addProcessor('topBar', thirdProcessor); + + remove(); + + expect(uut.getProcessors('topBar')).toEqual([firstProcessor, thirdProcessor]); + }); + + it('should ignore repeated subscription removal', () => { + const processor = (value: any, _commandName: string) => value; + const firstSubscription = uut.addProcessor('topBar', processor); + const secondSubscription = uut.addProcessor('topBar', processor); + + firstSubscription.remove(); + firstSubscription.remove(); + + expect(uut.getProcessors('topBar')).toEqual([processor]); + + secondSubscription.remove(); + expect(uut.getProcessors('topBar')).toEqual([]); + }); }); diff --git a/src/processors/OptionProcessorsStore.ts b/src/processors/OptionProcessorsStore.ts index 7b1d0d1df5..b0bf2ec9d1 100644 --- a/src/processors/OptionProcessorsStore.ts +++ b/src/processors/OptionProcessorsStore.ts @@ -2,7 +2,9 @@ import { ProcessorSubscription } from '../interfaces/ProcessorSubscription'; import { OptionsProcessor } from '../interfaces/Processors'; export class OptionProcessorsStore { - private optionsProcessorsByObjectPath: Record[]> = {}; + private optionsProcessorsByObjectPath: Record[]> = Object.create( + null + ); public addProcessor( optionPath: string, @@ -12,8 +14,16 @@ export class OptionProcessorsStore { this.optionsProcessorsByObjectPath[optionPath] = []; this.optionsProcessorsByObjectPath[optionPath].push(processor); + let removed = false; - return { remove: () => this.removeProcessor(optionPath, processor) }; + return { + remove: () => { + if (!removed) { + this.removeProcessor(optionPath, processor); + removed = true; + } + }, + }; } public getProcessors(optionPath: string) { @@ -21,8 +31,8 @@ export class OptionProcessorsStore { } private removeProcessor(optionPath: string, processor: OptionsProcessor) { - this.optionsProcessorsByObjectPath[optionPath].splice( - this.optionsProcessorsByObjectPath[optionPath].indexOf(processor) - ); + const processors = this.optionsProcessorsByObjectPath[optionPath]; + const index = processors.indexOf(processor); + if (index !== -1) processors.splice(index, 1); } }