diff --git a/packages/super-editor/src/editors/v1/components/toolbar/Toolbar.test.js b/packages/super-editor/src/editors/v1/components/toolbar/Toolbar.test.js new file mode 100644 index 0000000000..27f497687d --- /dev/null +++ b/packages/super-editor/src/editors/v1/components/toolbar/Toolbar.test.js @@ -0,0 +1,114 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { mount } from '@vue/test-utils'; +import { defineComponent, ref, KeepAlive } from 'vue'; +import Toolbar from './Toolbar.vue'; + +const ToolbarKeepAliveHost = defineComponent({ + components: { KeepAlive, Toolbar }, + setup() { + const visible = ref(true); + return { visible }; + }, + template: '', +}); + +function createMockToolbar() { + return { + config: { + toolbarGroups: ['left', 'center', 'right'], + toolbarButtonsExclude: [], + }, + getToolbarItemByGroup: () => [], + getToolbarItemByName: () => null, + onToolbarResize: vi.fn(), + emitCommand: vi.fn(), + overflowItems: [], + activeEditor: null, + }; +} + +describe('Toolbar', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('removes resize and keydown listeners on unmount (not only on KeepAlive deactivate)', () => { + const mockToolbar = createMockToolbar(); + const addSpy = vi.spyOn(window, 'addEventListener'); + const removeSpy = vi.spyOn(window, 'removeEventListener'); + + const wrapper = mount(Toolbar, { + global: { + stubs: { ButtonGroup: true }, + plugins: [ + (app) => { + app.config.globalProperties.$toolbar = mockToolbar; + }, + ], + }, + }); + + const resizeHandler = addSpy.mock.calls.find((c) => c[0] === 'resize')?.[1]; + const keydownHandler = addSpy.mock.calls.find((c) => c[0] === 'keydown')?.[1]; + expect(resizeHandler).toBeTypeOf('function'); + expect(keydownHandler).toBeTypeOf('function'); + + removeSpy.mockClear(); + wrapper.unmount(); + + expect(removeSpy).toHaveBeenCalledWith('resize', resizeHandler); + expect(removeSpy).toHaveBeenCalledWith('keydown', keydownHandler); + + addSpy.mockRestore(); + removeSpy.mockRestore(); + }); + + it('removes window listeners on KeepAlive deactivate and restores them on activate', async () => { + const mockToolbar = createMockToolbar(); + const addSpy = vi.spyOn(window, 'addEventListener'); + const removeSpy = vi.spyOn(window, 'removeEventListener'); + + const wrapper = mount(ToolbarKeepAliveHost, { + global: { + stubs: { ButtonGroup: true }, + plugins: [ + (app) => { + app.config.globalProperties.$toolbar = mockToolbar; + }, + ], + }, + }); + + const resizeHandler = addSpy.mock.calls.find((c) => c[0] === 'resize')?.[1]; + const keydownHandler = addSpy.mock.calls.find((c) => c[0] === 'keydown')?.[1]; + expect(resizeHandler).toBeTypeOf('function'); + expect(keydownHandler).toBeTypeOf('function'); + + addSpy.mockClear(); + removeSpy.mockClear(); + + wrapper.vm.visible = false; + await wrapper.vm.$nextTick(); + + expect(removeSpy).toHaveBeenCalledWith('resize', resizeHandler); + expect(removeSpy).toHaveBeenCalledWith('keydown', keydownHandler); + + addSpy.mockClear(); + removeSpy.mockClear(); + + wrapper.vm.visible = true; + await wrapper.vm.$nextTick(); + + expect(addSpy).toHaveBeenCalledWith('resize', resizeHandler); + expect(addSpy).toHaveBeenCalledWith('keydown', keydownHandler); + + removeSpy.mockClear(); + wrapper.unmount(); + + expect(removeSpy).toHaveBeenCalledWith('resize', resizeHandler); + expect(removeSpy).toHaveBeenCalledWith('keydown', keydownHandler); + + addSpy.mockRestore(); + removeSpy.mockRestore(); + }); +}); diff --git a/packages/super-editor/src/editors/v1/components/toolbar/Toolbar.vue b/packages/super-editor/src/editors/v1/components/toolbar/Toolbar.vue index d2a03efca6..59ecd7ace3 100644 --- a/packages/super-editor/src/editors/v1/components/toolbar/Toolbar.vue +++ b/packages/super-editor/src/editors/v1/components/toolbar/Toolbar.vue @@ -1,5 +1,14 @@