diff --git a/package.json b/package.json index 5b9bef2..c259827 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "zeed", "type": "module", - "version": "0.22.0", + "version": "0.22.1", "description": "🌱 Simple foundation library", "author": { "name": "Dirk Holtwick", @@ -62,14 +62,15 @@ "start": "nr watch", "test": "vitest", "test:browser": "BROWSER=1 vitest", + "test:firefox": "BROWSER=firefox vitest", + "test:webkit": "BROWSER=webkit vitest", + "test:chromium": "BROWSER=chromium vitest", "test:publish": "vitest --run", "watch": "nr build -- --watch src" }, "devDependencies": { "@antfu/eslint-config": "^2.21.3", "@antfu/ni": "^0.21.12", - "@testing-library/jest-dom": "^6.4.6", - "@testing-library/vue": "^8.1.0", "@types/node": "^20.14.10", "@vitejs/plugin-vue": "^5.0.5", "@vitest/browser": "^2.0.1", diff --git a/src/common/data/convert.spec.ts b/src/common/data/convert.spec.ts index 5944843..196b4ce 100644 --- a/src/common/data/convert.spec.ts +++ b/src/common/data/convert.spec.ts @@ -1,4 +1,4 @@ -import { renderMessages, valueToInteger, valueToString } from './convert' +import { renderMessages, valueToBoolean, valueToBooleanNotFalse, valueToInteger, valueToString } from './convert' describe('convert', () => { it('should convert', () => { @@ -30,4 +30,25 @@ describe('convert', () => { \"b\": null } null undefined NaN 100 string`) }) + + it('should have some bias', async () => { + expect(valueToBoolean('123')).toBe(false) + expect(valueToBoolean('1')).toBe(true) + expect(valueToBoolean('on')).toBe(true) + expect(valueToBoolean(1)).toBe(true) + expect(valueToBoolean(123)).toBe(true) // ??? + expect(valueToBoolean(undefined)).toBe(false) + expect(valueToBoolean(null)).toBe(false) + expect(valueToBoolean('')).toBe(false) + + expect(valueToBooleanNotFalse('123')).toBe(true) + expect(valueToBooleanNotFalse('1')).toBe(true) + expect(valueToBooleanNotFalse('0')).toBe(false) + expect(valueToBooleanNotFalse('off')).toBe(false) + expect(valueToBooleanNotFalse(1)).toBe(true) + expect(valueToBooleanNotFalse(123)).toBe(true) + expect(valueToBooleanNotFalse(undefined)).toBe(true) + expect(valueToBooleanNotFalse(null)).toBe(true) + expect(valueToBooleanNotFalse('')).toBe(true) + }) }) diff --git a/src/common/data/convert.ts b/src/common/data/convert.ts index 81db593..f487371 100644 --- a/src/common/data/convert.ts +++ b/src/common/data/convert.ts @@ -3,6 +3,7 @@ import { Uint8ArrayToHexDump } from './bin' // import { jsonStringify } from './json' const TRUE_VALUES_LIST = ['1', 'true', 'yes', 'y', 'on'] +const FALSE_VALUES_LIST = ['0', 'false', 'no', 'n', 'off'] export function stringToBoolean(value?: string, defaultValue = false): boolean { if (value == null || typeof value !== 'string') @@ -22,6 +23,7 @@ export function stringToFloat(value?: string, defaultValue = 0.0): number { return Number.parseFloat(value.trim()) ?? defaultValue } +/** `true` is a number != 0, a string stating `true`. Otherwise false. */ export function valueToBoolean(value?: any, defaultValue = false): boolean { if (value == null) return defaultValue @@ -32,6 +34,17 @@ export function valueToBoolean(value?: any, defaultValue = false): boolean { return TRUE_VALUES_LIST.includes(String(value).trim().toLowerCase()) } +/** Explicitly has to have a `false` value to become `false`, otherwise `true` */ +export function valueToBooleanNotFalse(value?: any, defaultValue = true): boolean { + if (value == null) + return defaultValue + if (typeof value === 'boolean') + return value + if (typeof value === 'number') + return value !== 0 + return !(FALSE_VALUES_LIST.includes(String(value).trim().toLowerCase())) +} + export function valueToInteger(value?: any, defaultValue = 0): number { if (value == null) return defaultValue diff --git a/src/common/dispose-defer.spec.ts b/src/common/dispose-defer.spec.ts index 39d11dc..051e155 100644 --- a/src/common/dispose-defer.spec.ts +++ b/src/common/dispose-defer.spec.ts @@ -1,5 +1,3 @@ -// (C)opyright 20210922 Dirk Holtwick, holtwick.it. All rights reserved. - import { DefaultLogger } from './log' import { useDefer, useDispose } from './dispose-defer' import { sleep } from './exec' @@ -138,6 +136,12 @@ describe('dispose', () => { // TODO future it('should use using', async () => { + const history: string[] = [] + + function log(s: string) { + history.push(s) + } + class TempFile implements Disposable { constructor(path: string) { log('constructor') @@ -156,5 +160,15 @@ describe('dispose', () => { log('fn before') fn() log('fn after') + + expect(history).toMatchInlineSnapshot(` + Array [ + "fn before", + "constructor", + "fn return", + "dispose", + "fn after", + ] + `) }) }) diff --git a/src/common/dispose-defer.ts b/src/common/dispose-defer.ts index 901a3b7..3c78b93 100644 --- a/src/common/dispose-defer.ts +++ b/src/common/dispose-defer.ts @@ -5,15 +5,18 @@ import { isPromise } from './exec/promise' import { DefaultLogger } from './log' import type { LoggerInterface } from './log/log-base' -// function polyfillUsing() { -// try { -// // @ts-expect-error just a polyfill -// Symbol.dispose ??= Symbol('Symbol.dispose') -// // @ts-expect-error just a polyfill -// Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose') -// } -// catch (err) { } -// } +export function polyfillUsing() { + try { + // @ts-expect-error just a polyfill + Symbol.dispose ??= Symbol('Symbol.dispose') + // @ts-expect-error just a polyfill + Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose') + } + catch (err) { } +} + +// Symbol.dispose ??= Symbol('Symbol.dispose') +// Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose') /** Different kinds of implementations have grown, this should unify them */ function callDisposer(disposable: Disposer): Promise | void { @@ -50,6 +53,8 @@ export function useDispose(config?: string | UseDisposeConfig | LoggerInterface) opt = { name: opt.label, log: opt } } + polyfillUsing() + const name = opt?.name const log = opt?.log ?? DefaultLogger('zeed:dispose') @@ -148,6 +153,8 @@ export function useDefer( const { mode = 'fifo' } = config const steps: Disposer[] = [] + polyfillUsing() + /** * Excutes all steps. If all steps are not Promises, they are executed immediately, * otherwise a Promise is returned diff --git a/src/common/dispose-utils.spec.ts b/src/common/dispose-utils.spec.ts index eab2573..f679be9 100644 --- a/src/common/dispose-utils.spec.ts +++ b/src/common/dispose-utils.spec.ts @@ -1,5 +1,6 @@ import { vi as jest } from 'vitest' import { useDisposeWithUtils, useEventListener, useTimeout } from './dispose-utils' +import { polyfillUsing } from './dispose-defer' describe('useTimeout', () => { it('should call the provided function after the specified timeout', () => { @@ -62,6 +63,8 @@ describe('useTimeout', () => { const fn = jest.fn() const args = [1, 2, 3] + polyfillUsing() // IMPORTANT! + function helper() { using _ = useEventListener(emitter, eventName, fn, ...args) as any expect(emitter.on).toBeCalledWith(eventName, fn, ...args) diff --git a/vitest.config.ts b/vitest.config.ts index b1bc93e..c6d37bb 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,25 +1,43 @@ /// +/* eslint-disable no-console */ import { resolve } from 'node:path' import process from 'node:process' import { defineConfig } from 'vite' import type { UserConfig } from 'vitest' - -const isBrowser = +(process.env.BROWSER ?? '0') +import { valueToBooleanNotFalse } from './src/common/data/convert' const config: UserConfig = { - // include: ['**/*.{client,test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - // exclude: ['node_modules', 'dist', '.idea', '.git', '.cache', '*/_archive/*'], snapshotFormat: { printBasicPrototype: true, }, - // root: './src', globals: true, alias: { '@/': `${resolve(process.cwd(), 'src')}/`, }, + // include: ['**/*.{client,test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + // root: './src', } +const isBrowser = process.env.BROWSER && valueToBooleanNotFalse(process.env.BROWSER) + +const browserName = { + c: 'chromium', + chromium: 'chromium', + chrome: 'chromium', + g: 'chromium', + google: 'chromium', + e: 'chromium', + edge: 'chromium', + w: 'webkit', + webkit: 'webkit', + s: 'webkit', + safari: 'webkit', + f: 'firefox', + firefox: 'firefox', +}[String(process.env.BROWSER).toLowerCase()] ?? 'chromium' + if (isBrowser) { + console.info('BROWSER', browserName, JSON.stringify(process.env.BROWSER)) Object.assign(config, { include: [ './src/browser/**/*.{client,test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', @@ -27,17 +45,21 @@ if (isBrowser) { ], browser: { enabled: true, - name: 'chromium', + name: browserName, provider: 'playwright', // https://playwright.dev - providerOptions: {}, + providerOptions: { + launch: { + devtools: true, + }, + }, }, }) } else { + console.info('NODE') + Object.assign(config, { setupFiles: ['vitest-setup.ts'], - // include: ['**/*.{client,test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - // exclude: ['node_modules', 'dist', '.idea', '.git', '.cache', '*/_archive/*'], include: [ './src/node/**/*.{client,test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', './src/common/**/*.{client,test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', @@ -48,6 +70,9 @@ else { export default defineConfig({ test: config, + // https://github.com/vitest-dev/vitest/issues/4183 - esbuild: { target: 'es2022' }, + esbuild: { + target: 'es2022', + }, })