improve types for events, configure codecov add fast test for setup update

This commit is contained in:
Rene Haas
2022-08-18 16:40:07 +02:00
parent 689ae664b6
commit e109c93ce7
12 changed files with 132 additions and 113 deletions
+4 -4
View File
@@ -23,12 +23,12 @@ jobs:
run: npm ci --install-links
- name: Run Jest
run: npm run jest
- uses: codecov/codecov-action@v3
with:
flags: unit
files: "**/jest/coverage*.json"
- name: Run Playwright
run: xvfb-run npm run playwright
- uses: codecov/codecov-action@v3
with:
flags: unit
files: "**/jest/coverage*.json"
- uses: codecov/codecov-action@v3
with:
flags: ui
+1 -2
View File
@@ -7,11 +7,10 @@
<a href="https://www.npmjs.com/package/overlayscrollbars"><img src="https://img.shields.io/npm/dm/overlayscrollbars.svg?style=flat-square" alt="Downloads"></a>
<a href="https://www.npmjs.com/package/overlayscrollbars"><img src="https://img.shields.io/npm/v/overlayscrollbars.svg?style=flat-square" alt="Version"></a>
<a href="https://github.com/KingSora/OverlayScrollbars/blob/master/LICENSE"><img src="https://img.shields.io/github/license/kingsora/overlayscrollbars.svg?style=flat-square" alt="License"></a>
<a href="https://app.codecov.io/gh/KingSora/OverlayScrollbars"><img src="https://img.shields.io/codecov/c/github/KingSora/OverlayScrollbars?style=flat-square" alt="Code Coverage"></a>
<a href="https://bundlephobia.com/package/overlayscrollbars"><img src="https://img.shields.io/bundlephobia/minzip/overlayscrollbars?label=max.%20bundle%20size&style=flat-square" alt="Max. Bundle Size"></a>
</h6>
> OverlayScrollbars is a javascript scrollbar plugin that hides native scrollbars, provides custom styleable overlay scrollbars and keeps the native functionality and feeling.
> This is the documentation for version `2.x`. which is currently in beta. You can read the version `1.x` docs [here](https://github.com/KingSora/OverlayScrollbars/tree/v1.x) or on the [website](https://kingsora.github.io/OverlayScrollbars/#!overview).
+3 -1
View File
@@ -91,7 +91,9 @@ export const iterateSelect = async <T>(
if (select) {
const { beforeEach = noop, check = noop, afterEach = noop, filter } = options || {};
const selectOptions = getSelectOptions(select);
const selectOptionsReversed = getSelectOptions(select).reverse();
const selectOptionsReversed = getSelectOptions(select)
.reverse()
.filter((_, i) => i > 0);
const iterateOptions = [...selectOptions, ...selectOptionsReversed].filter(
filter || (() => true)
);
+1 -1
View File
@@ -1,4 +1,4 @@
declare module '@~local/playwright-tooling' {
export function playwrightRollup(): void;
export function expectSuccess(page: any): void;
export function expectSuccess(page: any): Promise<void>;
}
+2 -2
View File
@@ -7,14 +7,14 @@
<a href="https://www.npmjs.com/package/overlayscrollbars"><img src="https://img.shields.io/npm/dm/overlayscrollbars.svg?style=flat-square" alt="Downloads"></a>
<a href="https://www.npmjs.com/package/overlayscrollbars"><img src="https://img.shields.io/npm/v/overlayscrollbars.svg?style=flat-square" alt="Version"></a>
<a href="https://github.com/KingSora/OverlayScrollbars/blob/master/LICENSE"><img src="https://img.shields.io/github/license/kingsora/overlayscrollbars.svg?style=flat-square" alt="License"></a>
<a href="https://app.codecov.io/gh/KingSora/OverlayScrollbars"><img src="https://img.shields.io/codecov/c/github/KingSora/OverlayScrollbars?style=flat-square" alt="Code Coverage"></a>
<a href="https://bundlephobia.com/package/overlayscrollbars"><img src="https://img.shields.io/bundlephobia/minzip/overlayscrollbars?label=max.%20bundle%20size&style=flat-square" alt="Max. Bundle Size"></a>
</h6>
> OverlayScrollbars is a javascript scrollbar plugin that hides native scrollbars, provides custom styleable overlay scrollbars and keeps the native functionality and feeling.
> This is the documentation for version `2.x`. which is currently in beta. You can read the version `1.x` docs [here](https://github.com/KingSora/OverlayScrollbars/tree/v1.x) or on the [website](https://kingsora.github.io/OverlayScrollbars/#!overview).
## Why
I've created this plugin because I hate ugly and space consuming scrollbars. Similar plugins haven't met my requirements in terms of features, quality, simplicity, license or browser support.
+2 -2
View File
@@ -42,11 +42,11 @@
"sideEffects": ["*.css", "*.scss", "*.sass"],
"scripts": {
"build": "rollup -c",
"test": "jest --coverage && playwright test",
"test": "jest --coverage && playwright test --grep-invert @special",
"jest": "jest --coverage --testPathPattern",
"jest:node": "jest --selectProjects node --testPathPattern",
"jest:jsdom": "jest --selectProjects jsdom --testPathPattern",
"playwright": "playwright test",
"playwright": "playwright test --grep-invert @special",
"playwright:dev": "playwright test --workers 1 --timeout 0 --global-timeout 0",
"posttest": "playwright-merge-coverage && full-coverage",
"postjest": "full-coverage",
@@ -2,18 +2,17 @@ import { isArray } from 'support/utils/types';
import { keys } from 'support/utils/object';
import { each, from, isEmptyArray } from 'support/utils/array';
export type EventListener<
EventMap extends Record<string, any[]>,
N extends keyof EventMap = keyof EventMap
> = (...args: EventMap[N]) => void;
export type EventListener<EventMap extends Record<string, any[]>, N extends keyof EventMap> = (
...args: EventMap[N]
) => void;
export type InitialEventListeners<EventMap extends Record<string, any[]>> = {
[K in keyof EventMap]?: EventListener<EventMap> | EventListener<EventMap>[];
[K in keyof EventMap]?: EventListener<EventMap, K> | EventListener<EventMap, K>[];
};
const manageListener = <EventMap extends Record<string, any[]>>(
callback: (listener?: EventListener<EventMap>) => void,
listener?: EventListener<EventMap> | EventListener<EventMap>[]
const manageListener = <EventMap extends Record<string, any[]>, N extends keyof EventMap>(
callback: (listener?: EventListener<EventMap, N>) => void,
listener?: EventListener<EventMap, N> | EventListener<EventMap, N>[]
) => {
each(isArray(listener) ? listener : [listener], callback);
};
@@ -22,7 +21,7 @@ export const createEventListenerHub = <EventMap extends Record<string, any[]>>(
initialEventListeners?: InitialEventListeners<EventMap>
) => {
// eslint-disable-next-line @typescript-eslint/no-shadow
type EventListener<N extends keyof EventMap = keyof EventMap> = (...args: EventMap[N]) => void;
type EventListener<N extends keyof EventMap> = (...args: EventMap[N]) => void;
type RemoveEvent = {
<N extends keyof EventMap>(name?: N, listener?: EventListener<N>): void;
<N extends keyof EventMap>(name?: N, listener?: EventListener<N>[]): void;
@@ -40,7 +39,7 @@ export const createEventListenerHub = <EventMap extends Record<string, any[]>>(
<N extends keyof EventMap>(name: N, args?: EventMap[N]): void;
};
const events = new Map<keyof EventMap, Set<EventListener>>();
const events = new Map<keyof EventMap, Set<EventListener<keyof EventMap>>>();
const removeEvent: RemoveEvent = <N extends keyof EventMap>(
name?: N,
@@ -50,9 +49,9 @@ export const createEventListenerHub = <EventMap extends Record<string, any[]>>(
const eventSet = events.get(name);
manageListener((currListener) => {
if (eventSet) {
eventSet[currListener ? 'delete' : 'clear'](currListener!);
eventSet[currListener ? 'delete' : 'clear'](currListener! as any);
}
}, listener as any);
}, listener);
} else {
events.forEach((eventSet) => {
eventSet.clear();
@@ -1,14 +1,14 @@
import { createEventListenerHub } from 'support/eventListeners';
type EventMap = {
onBoolean: [boolean, string];
onBoolean: [a: boolean, b: string];
onUndefined: [];
};
describe('eventListeners', () => {
describe('createEventListenerHub', () => {
test('initialization', () => {
const onBooleanA = jest.fn();
const onBooleanA = jest.fn((a: boolean, b: string) => a + b);
const onBooleanB = jest.fn();
const onUndefined = jest.fn();
const [, , triggerEvent] = createEventListenerHub<EventMap>({
@@ -28,7 +28,7 @@ describe('eventListeners', () => {
});
test('addEvent', () => {
const onBooleanA = jest.fn();
const onBooleanA = jest.fn((a: boolean, b: string) => a + b);
const onBooleanB = jest.fn();
const onUndefinedA = jest.fn();
const onUndefinedB = jest.fn();
@@ -6,6 +6,7 @@ import { addClass } from 'support';
const params = url.searchParams;
/**
* fast: faster but not so accurate run
* nsh: native scrollbar hiding
* fbg: flexbox glue
* ccp: css custom props
@@ -14,7 +15,7 @@ import { addClass } from 'support';
* vpt: viewport is target
* pa: padding absolute
*/
['nsh', 'fbg', 'ccp', 'po', 'fo', 'vpt', 'pa'].forEach((param) => {
['fast', 'nsh', 'fbg', 'ccp', 'po', 'fo', 'vpt', 'pa'].forEach((param) => {
const paramValue = Boolean(params.get(param));
if (paramValue) {
@@ -27,6 +27,8 @@ import {
createDOM,
hasClass,
createDiv,
removeElements,
removeClass,
} from 'support';
import { Options } from 'options';
import { DeepPartial } from 'typings';
@@ -77,7 +79,6 @@ interface Metrics {
height: number;
};
}
interface CheckComparisonObj {
updCount: number;
metrics: Metrics;
@@ -102,7 +103,8 @@ const msedge = window.navigator.userAgent.toLowerCase().indexOf('edge') > -1;
msie11 && addClass(document.body, 'msie11');
const paddingAbsolute = hasClass(document.body, 'pa');
const initialPaddingAbsolute = hasClass(document.body, 'pa');
const isFastTestRun = hasClass(document.body, 'fast');
const useContentElement = false;
const fixedDigits = msie11 ? 1 : 3;
const fixedDigitsOffset = 3;
@@ -122,6 +124,8 @@ const targetOptionsSlot: HTMLElement | null = document.querySelector('#options')
const targetUpdatesSlot: HTMLElement | null = document.querySelector('#updates');
const comparisonContentElm: HTMLElement = document.createElement('div');
const envElms = document.querySelectorAll<HTMLElement>('.env');
const getComparisonViewport = () =>
(comparison?.querySelector(`.${classNameViewport}`) || comparison) as HTMLElement;
const initObj = hasClass(document.body, 'vpt')
? {
@@ -146,19 +150,37 @@ const osInstance =
(window.os = OverlayScrollbars(
initObj,
{
paddingAbsolute,
paddingAbsolute: initialPaddingAbsolute,
},
{
updated() {
updated(instance) {
updateCount++;
const { paddingAbsolute, overflow } = instance.options();
const comparisonViewport = getComparisonViewport();
if (paddingAbsolute) {
if (comparisonViewport === comparison) {
addClass(document.body, 'pa');
const absoluteWrapper = createDiv(classNameViewport);
appendChildren(absoluteWrapper, contents(comparison));
appendChildren(comparison, absoluteWrapper);
}
} else if (comparisonViewport !== comparison) {
removeClass(document.body, 'pa');
appendChildren(comparison, contents(comparisonViewport));
removeElements(comparisonViewport);
}
requestAnimationFrame(() => {
if (targetUpdatesSlot) {
targetUpdatesSlot.textContent = `${updateCount}`;
}
if (targetOptionsSlot) {
targetOptionsSlot.textContent = JSON.stringify(
assignDeep({}, osInstance.options().overflow, {
paddingAbsolute: osInstance.options().paddingAbsolute,
assignDeep({}, overflow, {
paddingAbsolute,
viewportIsTarget: instance.elements().viewport === target,
}),
null,
2
@@ -169,8 +191,6 @@ const osInstance =
}
));
const getComparisonViewport = () =>
paddingAbsolute ? (comparison!.firstElementChild as HTMLElement) : comparison;
const getMetrics = (elm: HTMLElement): Metrics => {
// const rounding = isFractionalPixelRatio() ? Math.round : (num: number) => num;
const elmIsTarget = elm === target;
@@ -185,7 +205,6 @@ const getMetrics = (elm: HTMLElement): Metrics => {
width: Math.max(0, comparisonViewport!.scrollWidth - comparisonViewport!.clientWidth),
height: Math.max(0, comparisonViewport!.scrollHeight - comparisonViewport!.clientHeight),
};
console.log(elm, amount, comparisonViewport);
return {
width: condition(amount.width) ? amount.width : 0,
height: condition(amount.height) ? amount.height : 0,
@@ -348,8 +367,6 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
const scrollWidth = targetMetrics.scroll.width - comparisonMetrics.scroll.width;
const scrollHeight = targetMetrics.scroll.height - comparisonMetrics.scroll.height;
if (scrollWidth !== 0 || scrollHeight !== 0) {
console.log(scrollWidth);
console.log(scrollHeight);
roundingElm!.textContent = `${(parseInt(roundingElm!.textContent || '0', 10) || 0) + 1}`;
}
} else {
@@ -550,10 +567,10 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
const iterate = async (
select: HTMLSelectElement | null,
afterEach?: () => any,
skippedItems?: string[]
skippedItems?: string[] | false | null
) => {
await iterateSelect<CheckComparisonObj>(select, {
filter: (item: string) => !skippedItems?.includes(item),
filter: skippedItems ? (item: string) => !skippedItems?.includes(item) : undefined,
beforeEach() {
const metrics = getMetrics(comparison!);
return {
@@ -575,10 +592,10 @@ const iterateEnvHeight = async (afterEach?: () => any) => {
await iterate(envHeightSelect, afterEach);
};
*/
const iterateHeight = async (afterEach?: () => any, skippedItems?: string[]) => {
const iterateHeight = async (afterEach?: () => any, skippedItems?: string[] | false | null) => {
await iterate(containerHeightSelect, afterEach, skippedItems);
};
const iterateWidth = async (afterEach?: () => any, skippedItems?: string[]) => {
const iterateWidth = async (afterEach?: () => any, skippedItems?: string[] | false | null) => {
await iterate(containerWidthSelect, afterEach, skippedItems);
};
/*
@@ -586,10 +603,10 @@ const iterateFloat = async (afterEach?: () => any) => {
await iterate(containerFloatSelect, afterEach);
};
*/
const iteratePadding = async (afterEach?: () => any, skippedItems?: string[]) => {
const iteratePadding = async (afterEach?: () => any, skippedItems?: string[] | false | null) => {
await iterate(containerPaddingSelect, afterEach, skippedItems);
};
const iterateBorder = async (afterEach?: () => any, skippedItems?: string[]) => {
const iterateBorder = async (afterEach?: () => any, skippedItems?: string[] | false | null) => {
await iterate(containerBorderSelect, afterEach, skippedItems);
};
/*
@@ -765,45 +782,34 @@ const overflowTest = async (osOptions?: DeepPartial<Options>) => {
removeAttr(comparisonEnd, 'style');
};
const withSkippedItems = osOptions || isFastTestRun;
if (osOptions) {
osInstance.options(osOptions);
await iterateMinMax(async () => {
await iterateBoxSizing(async () => {
await iterateHeight(async () => {
await iterateWidth(async () => {
await iterateBorder(async () => {
await iteratePadding(async () => {
await iterateOverflow();
}, ['paddingLarge']);
}, ['borderSmall']);
}, ['widthHundred']);
}, ['heightHundred']);
});
});
} else {
await iterateMinMax(async () => {
await iterateBoxSizing(async () => {
await iterateHeight(async () => {
await iterateWidth(async () => {
await iterateBorder(async () => {
// assume this part isn't critical
/*
await iterateFloat(async () => {
await iterateMargin();
});
*/
await iteratePadding(async () => {
await iterateOverflow();
});
await iterateDirection();
});
});
});
});
});
}
await iterateMinMax(async () => {
await iterateBoxSizing(async () => {
await iterateHeight(async () => {
await iterateWidth(async () => {
await iterateBorder(async () => {
// assume this part isn't critical
// await iterateFloat(async () => {
// await iterateMargin();
// });
await iteratePadding(async () => {
await iterateOverflow();
}, withSkippedItems && ['paddingLarge']);
// assume this part isn't critical for special options
if (!osOptions) {
await iterateDirection();
}
}, withSkippedItems && ['borderSmall']);
}, withSkippedItems && ['widthHundred']);
}, withSkippedItems && ['heightHundred']);
});
});
};
const start = async () => {
@@ -812,17 +818,28 @@ const start = async () => {
target?.removeAttribute('style');
try {
await overflowTest();
osInstance.options({ paddingAbsolute: !initialPaddingAbsolute });
await overflowTest();
osInstance.options({ paddingAbsolute: initialPaddingAbsolute });
await overflowTest({ overflow: { x: 'visible', y: 'visible' } });
await overflowTest({ overflow: { x: 'visible-scroll', y: 'visible-hidden' } });
await overflowTest({ overflow: { x: 'visible-hidden', y: 'hidden' } });
await overflowTest({ overflow: { x: 'visible', y: 'visible-scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'visible-scroll' } });
await overflowTest({ overflow: { x: 'hidden', y: 'scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'hidden' } });
await overflowTest({ overflow: { x: 'visible', y: 'scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'visible' } });
await overflowTest({ overflow: { x: 'visible', y: 'hidden' } });
await overflowTest({ overflow: { x: 'hidden', y: 'visible' } });
await overflowTest({ overflow: { x: 'visible-hidden', y: 'scroll' } });
await overflowTest({ overflow: { x: 'visible-scroll', y: 'visible-hidden' } });
if (!isFastTestRun) {
await overflowTest({ overflow: { x: 'hidden', y: 'visible' } });
await overflowTest({ overflow: { x: 'visible', y: 'scroll' } });
await overflowTest({ overflow: { x: 'visible-hidden', y: 'hidden' } });
await overflowTest({ overflow: { x: 'visible', y: 'visible-scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'visible-scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'hidden' } });
await overflowTest({ overflow: { x: 'scroll', y: 'visible' } });
await overflowTest({ overflow: { x: 'visible', y: 'hidden' } });
}
} catch (e) {
console.log(e);
}
@@ -842,10 +859,3 @@ if (!useContentElement) {
appendChildren(comparison, comparisonContentElm);
appendChildren(comparisonContentElm, elms);
}
if (paddingAbsolute) {
const absoluteWrapper = createDiv(classNameViewport);
appendChildren(absoluteWrapper, contents(comparison));
appendChildren(comparison, absoluteWrapper);
}
@@ -1,4 +1,5 @@
<div id="controls">
<button id="fast">Fast</button>
<button id="nsh">Without Native Scrollbar Hiding</button>
<button id="fbg">Without Flexbox Glue</button>
<button id="ccp">Without CSS Custom Props</button>
@@ -3,12 +3,17 @@ import { test, Page } from '@playwright/test';
playwrightRollup();
test.describe('StructureSetup.update', () => {
[false].forEach((targetIsViewport) => {
const createTests = (fast?: boolean) => {
[false, true].forEach((targetIsViewport) => {
const isOrIsNot = targetIsViewport ? 'is' : 'is not';
const setTargetIsVp = async (page: Page) => {
if (targetIsViewport) {
await page.click('#tvp');
await page.click('#vpt');
}
};
const setFast = async (page: Page) => {
if (fast || targetIsViewport) {
await page.click('#fast');
}
};
@@ -21,20 +26,20 @@ test.describe('StructureSetup.update', () => {
}
};
test.beforeEach(async ({ page }) => {
await setFast(page);
await setTargetIsVp(page);
await nsh(page);
});
test.describe(`${withText} native scrollbar styling`, () => {
test.describe.configure({ mode: 'parallel' });
// test.describe.configure({ mode: 'parallel' });
test('default', async ({ page }) => {
await setTargetIsVp(page);
await nsh(page);
await expectSuccess(page);
});
test('with fully overlaid scrollbars', async ({ page }) => {
await setTargetIsVp(page);
await nsh(page);
await page.click('#fo');
await expectSuccess(page);
@@ -46,18 +51,12 @@ test.describe('StructureSetup.update', () => {
"firefox can't simulate partially overlaid scrollbars, boost speed by omitting webkit"
);
await setTargetIsVp(page);
await nsh(page);
await page.click('#po');
await expectSuccess(page);
});
test('without flexbox glue & css custom props', async ({ page }) => {
await setTargetIsVp(page);
await nsh(page);
await page.click('#fbg');
await page.click('#ccp');
@@ -67,4 +66,12 @@ test.describe('StructureSetup.update', () => {
});
});
});
};
test.describe('StructureSetup.update @special', () => {
createTests();
});
test.describe('StructureSetup.update', () => {
createTests(true);
});