mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-08 22:12:28 +03:00
369 lines
11 KiB
TypeScript
369 lines
11 KiB
TypeScript
import { describe, test, expect, afterEach, vitest } from 'vitest';
|
|
import { cleanup, render, screen, fireEvent } from '@testing-library/svelte';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { OverlayScrollbars } from 'overlayscrollbars';
|
|
import { OverlayScrollbarsComponent } from '~/index'; // eslint-disable-line import/named
|
|
import type { OverlayScrollbarsComponentRef$ } from '~/OverlayScrollbarsComponent.types'; // eslint-disable-line import/named
|
|
import Test from './Test.svelte';
|
|
|
|
/**
|
|
* rerender would unmount and re-mount component... so I am faking it with custom event...
|
|
*/
|
|
describe('OverlayScrollbarsComponent', () => {
|
|
afterEach(() => cleanup());
|
|
|
|
describe('correct rendering', () => {
|
|
test('correct root element with instance', async () => {
|
|
const elementA = 'code';
|
|
const elementB = 'span';
|
|
let osInstance;
|
|
const { container } = render(Test);
|
|
const realContainer = container.firstElementChild!;
|
|
|
|
expect(realContainer).not.toBeEmptyDOMElement();
|
|
expect(realContainer.querySelector('div')).toBe(realContainer.firstElementChild); // default is div
|
|
|
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false);
|
|
osInstance = OverlayScrollbars(realContainer.firstElementChild as HTMLElement);
|
|
|
|
expect(osInstance).toBeDefined();
|
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
|
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: { element: elementA },
|
|
})
|
|
);
|
|
expect(realContainer.querySelector(elementA)).toBe(realContainer.firstElementChild);
|
|
|
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false); // prev instance is destroyed
|
|
osInstance = OverlayScrollbars(realContainer.firstElementChild as HTMLElement);
|
|
expect(osInstance).toBeDefined();
|
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
|
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: { element: elementB },
|
|
})
|
|
);
|
|
expect(realContainer.querySelector(elementB)).toBe(realContainer.firstElementChild);
|
|
|
|
expect(OverlayScrollbars.valid(osInstance)).toBe(false); // prev instance is destroyed
|
|
osInstance = OverlayScrollbars(realContainer.firstElementChild as HTMLElement);
|
|
expect(osInstance).toBeDefined();
|
|
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
|
|
});
|
|
|
|
test('data-overlayscrollbars-initialize', async () => {
|
|
const { container } = render(OverlayScrollbarsComponent);
|
|
|
|
expect(container.querySelector('[data-overlayscrollbars-initialize]')).toBeTruthy();
|
|
});
|
|
|
|
test('children', () => {
|
|
const { container } = render(Test);
|
|
expect(screen.getByText(/hello/)).toBeInTheDocument();
|
|
expect(screen.getByText(/svelte/)).toBeInTheDocument();
|
|
expect(screen.getByText(/svelte/).parentElement).not.toBe(container.firstElementChild);
|
|
});
|
|
|
|
test('dynamic children', async () => {
|
|
render(Test);
|
|
const addBtn = screen.getByText('add');
|
|
const removeBtn = screen.getByText('remove');
|
|
const initialElement = screen.getByText('0');
|
|
expect(initialElement).toBeInTheDocument();
|
|
|
|
const initialElementParent = initialElement.parentElement;
|
|
expect(initialElementParent).toBeInTheDocument();
|
|
|
|
userEvent.click(addBtn);
|
|
expect((await screen.findByText('1')).parentElement).toBe(initialElementParent);
|
|
|
|
userEvent.click(removeBtn);
|
|
userEvent.click(removeBtn);
|
|
expect((await screen.findByText('empty')).parentElement).toBe(initialElementParent);
|
|
});
|
|
|
|
test('className', async () => {
|
|
const { container } = render(Test, {
|
|
props: {
|
|
className: 'overlay scrollbars',
|
|
},
|
|
});
|
|
const realContainer = container.firstElementChild!;
|
|
|
|
expect(realContainer.firstElementChild).toHaveClass('overlay', 'scrollbars');
|
|
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: { className: 'overlay scrollbars svelte' },
|
|
})
|
|
);
|
|
|
|
expect(realContainer.firstElementChild).toHaveClass('overlay', 'scrollbars', 'svelte');
|
|
});
|
|
|
|
test('style', async () => {
|
|
const { container } = render(Test, {
|
|
props: {
|
|
style: 'width: 22px',
|
|
},
|
|
});
|
|
const realContainer = container.firstElementChild!;
|
|
|
|
expect(realContainer.firstElementChild).toHaveStyle({ width: '22px' });
|
|
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: { style: 'height: 33px' },
|
|
})
|
|
);
|
|
|
|
expect(realContainer.firstElementChild).toHaveStyle({ height: '33px' });
|
|
});
|
|
});
|
|
|
|
test('ref', () => {
|
|
let osRef: OverlayScrollbarsComponentRef$ | undefined;
|
|
const { container } = render(Test, {
|
|
props: {
|
|
getRef: (ref: any) => {
|
|
osRef = ref;
|
|
},
|
|
},
|
|
});
|
|
const realContainer = container.firstElementChild!;
|
|
|
|
expect(osRef).toBeTruthy();
|
|
|
|
const { osInstance, getElement } = osRef!;
|
|
expect(osInstance).toBeTypeOf('function');
|
|
expect(getElement).toBeTypeOf('function');
|
|
expect(OverlayScrollbars.valid(osInstance())).toBe(true);
|
|
expect(getElement()).toBe(realContainer.firstElementChild);
|
|
});
|
|
|
|
test('options', async () => {
|
|
let osRef: OverlayScrollbarsComponentRef$ | undefined;
|
|
render(Test, {
|
|
props: {
|
|
options: { paddingAbsolute: true, overflow: { y: 'hidden' } },
|
|
getRef: (ref: any) => {
|
|
osRef = ref;
|
|
},
|
|
},
|
|
});
|
|
|
|
const instance = osRef!.osInstance()!;
|
|
|
|
const opts = instance.options();
|
|
expect(opts.paddingAbsolute).toBe(true);
|
|
expect(opts.overflow.y).toBe('hidden');
|
|
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: {
|
|
options: { overflow: { x: 'hidden' } },
|
|
},
|
|
})
|
|
);
|
|
|
|
const newOpts = instance.options();
|
|
expect(newOpts.paddingAbsolute).toBe(false); //switches back to default because its not specified in the new options
|
|
expect(newOpts.overflow.x).toBe('hidden');
|
|
expect(newOpts.overflow.y).toBe('scroll'); //switches back to default because its not specified in the new options
|
|
|
|
// instance didn't change
|
|
expect(instance).toBe(osRef!.osInstance());
|
|
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: {
|
|
element: 'span',
|
|
options: { overflow: { x: 'hidden', y: 'hidden' } },
|
|
},
|
|
})
|
|
);
|
|
|
|
const newElementInstance = osRef!.osInstance()!;
|
|
const newElementNewOpts = newElementInstance.options();
|
|
expect(newElementInstance).not.toBe(instance);
|
|
expect(newElementNewOpts.paddingAbsolute).toBe(false);
|
|
expect(newElementNewOpts.overflow.x).toBe('hidden');
|
|
expect(newElementNewOpts.overflow.y).toBe('hidden');
|
|
|
|
// reset options with `undefined`, `null`, `false` or `{}`
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: {
|
|
options: undefined,
|
|
},
|
|
})
|
|
);
|
|
|
|
const clearedOpts = newElementInstance.options();
|
|
expect(osRef!.osInstance()).toBe(newElementInstance);
|
|
expect(clearedOpts.paddingAbsolute).toBe(false);
|
|
expect(clearedOpts.overflow.x).toBe('scroll');
|
|
expect(clearedOpts.overflow.y).toBe('scroll');
|
|
});
|
|
|
|
test('events', async () => {
|
|
const onUpdatedInitial = vitest.fn();
|
|
const onUpdated = vitest.fn();
|
|
let osRef: OverlayScrollbarsComponentRef$ | undefined;
|
|
render(Test, {
|
|
props: {
|
|
events: { updated: onUpdatedInitial },
|
|
getRef: (ref: any) => {
|
|
osRef = ref;
|
|
},
|
|
},
|
|
});
|
|
|
|
const instance = osRef!.osInstance()!;
|
|
|
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(1);
|
|
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: {
|
|
events: { updated: onUpdated },
|
|
},
|
|
})
|
|
);
|
|
expect(onUpdated).not.toHaveBeenCalled();
|
|
|
|
instance.update(true);
|
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(1);
|
|
expect(onUpdated).toHaveBeenCalledTimes(1);
|
|
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: {
|
|
events: { updated: [onUpdated, onUpdatedInitial] },
|
|
},
|
|
})
|
|
);
|
|
|
|
instance.update(true);
|
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(2);
|
|
expect(onUpdated).toHaveBeenCalledTimes(2);
|
|
|
|
// unregister with `[]`, `null` or `undefined`
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: {
|
|
events: { updated: null },
|
|
},
|
|
})
|
|
);
|
|
|
|
instance.update(true);
|
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(2);
|
|
expect(onUpdated).toHaveBeenCalledTimes(2);
|
|
|
|
// instance didn't change
|
|
expect(instance).toBe(osRef!.osInstance());
|
|
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: { element: 'span', events: { updated: [onUpdated, onUpdatedInitial] } },
|
|
})
|
|
);
|
|
|
|
const newElementInstance = osRef!.osInstance()!;
|
|
expect(newElementInstance).not.toBe(instance);
|
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(3);
|
|
expect(onUpdated).toHaveBeenCalledTimes(3);
|
|
|
|
// reset events with `undefined`, `null`, `false` or `{}`
|
|
await fireEvent(
|
|
screen.getByText('props'),
|
|
new CustomEvent('osProps', {
|
|
detail: { element: 'span', events: undefined },
|
|
})
|
|
);
|
|
|
|
newElementInstance.update(true);
|
|
expect(newElementInstance).toBe(osRef!.osInstance());
|
|
expect(onUpdatedInitial).toHaveBeenCalledTimes(3);
|
|
expect(onUpdated).toHaveBeenCalledTimes(3);
|
|
});
|
|
|
|
test('destroy', () => {
|
|
let osRef: OverlayScrollbarsComponentRef$ | undefined;
|
|
const { unmount } = render(Test, {
|
|
props: {
|
|
getRef(ref: any) {
|
|
osRef = ref;
|
|
},
|
|
},
|
|
});
|
|
|
|
const { osInstance } = osRef!;
|
|
|
|
expect(OverlayScrollbars.valid(osInstance())).toBe(true);
|
|
|
|
unmount();
|
|
|
|
expect(osInstance()).toBeDefined();
|
|
expect(OverlayScrollbars.valid(osInstance())).toBe(false);
|
|
});
|
|
|
|
test('dispatch events', async () => {
|
|
const initialized = vitest.fn((e: any) => {
|
|
const args = e.detail;
|
|
expect(args).toEqual([expect.any(Object)]);
|
|
});
|
|
const updated = vitest.fn((e: any) => {
|
|
const args = e.detail;
|
|
expect(args).toEqual([expect.any(Object), expect.any(Object)]);
|
|
});
|
|
const destroyed = vitest.fn((e: any) => {
|
|
const args = e.detail;
|
|
expect(args).toEqual([expect.any(Object), expect.any(Boolean)]);
|
|
});
|
|
const scroll = vitest.fn((e: any) => {
|
|
const args = e.detail;
|
|
expect(args).toEqual([expect.any(Object), expect.any(Event)]);
|
|
});
|
|
const { container, unmount } = render(Test, {
|
|
props: {
|
|
initialized,
|
|
updated,
|
|
destroyed,
|
|
scroll,
|
|
},
|
|
});
|
|
|
|
expect(initialized).toHaveBeenCalledTimes(1);
|
|
expect(updated).toHaveBeenCalledTimes(1);
|
|
expect(destroyed).not.toHaveBeenCalled();
|
|
expect(scroll).not.toHaveBeenCalled();
|
|
|
|
container.querySelectorAll('*').forEach((e) => {
|
|
fireEvent.scroll(e);
|
|
});
|
|
|
|
expect(destroyed).not.toHaveBeenCalled();
|
|
expect(scroll).toHaveBeenCalledTimes(1);
|
|
|
|
unmount();
|
|
|
|
expect(destroyed).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|