working rss feed to nostr publish
This commit is contained in:
+5
@@ -0,0 +1,5 @@
|
||||
// utils / compose
|
||||
|
||||
export const compose = (...fns) => {
|
||||
return fns.reduce((f, g) => (x) => f(g(x)))
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// compose.test
|
||||
|
||||
/* eslint-env jest */
|
||||
|
||||
import {
|
||||
compose
|
||||
} from './compose.js'
|
||||
|
||||
describe('test .compose() method:', () => {
|
||||
const f1 = (name) => {
|
||||
return `f1 ${name}`
|
||||
}
|
||||
const f2 = (name) => {
|
||||
return `f2 ${name}`
|
||||
}
|
||||
const f3 = (name) => {
|
||||
return `f3 ${name}`
|
||||
}
|
||||
|
||||
const addDashes = compose(f1, f2, f3)
|
||||
|
||||
const add3 = (num) => {
|
||||
return num + 3
|
||||
}
|
||||
|
||||
const mul6 = (num) => {
|
||||
return num * 6
|
||||
}
|
||||
|
||||
const div2 = (num) => {
|
||||
return num / 2
|
||||
}
|
||||
|
||||
const sub5 = (num) => {
|
||||
return num - 5
|
||||
}
|
||||
|
||||
const calculate = compose(sub5, div2, mul6, add3)
|
||||
|
||||
test(' check if .compose() works correctly', () => {
|
||||
expect(addDashes('Alice')).toEqual('f1 f2 f3 Alice')
|
||||
expect(calculate(5)).toEqual(19)
|
||||
})
|
||||
})
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// utils / curry
|
||||
|
||||
export const curry = (fn) => {
|
||||
const totalArguments = fn.length
|
||||
const next = (argumentLength, rest) => {
|
||||
if (argumentLength > 0) {
|
||||
return (...args) => {
|
||||
return next(argumentLength - args.length, [...rest, ...args])
|
||||
}
|
||||
}
|
||||
return fn(...rest)
|
||||
}
|
||||
return next(totalArguments, [])
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// curry.test
|
||||
|
||||
/* eslint-env jest */
|
||||
|
||||
import {
|
||||
curry
|
||||
} from './curry.js'
|
||||
|
||||
describe('test .curry() method:', () => {
|
||||
const sum = curry((a, b, c) => {
|
||||
return a + b + c
|
||||
})
|
||||
test(' check if .curry() works correctly', () => {
|
||||
expect(sum(3)(2)(1)).toEqual(6)
|
||||
expect(sum(1)(2)(3)).toEqual(6)
|
||||
expect(sum(1, 2)(3)).toEqual(6)
|
||||
expect(sum(1)(2, 3)).toEqual(6)
|
||||
expect(sum(1, 2, 3)).toEqual(6)
|
||||
})
|
||||
})
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// utils / date
|
||||
|
||||
import {
|
||||
isObject
|
||||
} from './detection.js'
|
||||
|
||||
const getDateFormat = () => {
|
||||
return {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'long',
|
||||
}
|
||||
}
|
||||
|
||||
const getTimeConvers = () => {
|
||||
return {
|
||||
second: 1000,
|
||||
minute: 60,
|
||||
hour: 60,
|
||||
day: 24,
|
||||
week: 7,
|
||||
month: 4,
|
||||
year: 12,
|
||||
}
|
||||
}
|
||||
|
||||
const isValidLocal = (hl) => {
|
||||
try {
|
||||
const locale = new Intl.Locale(hl)
|
||||
return locale.language !== ''
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const formatDateString = (...args) => {
|
||||
const input = args[0]
|
||||
const lang = isValidLocal(args[1]) ? args[1] : 'en'
|
||||
const dfmt = getDateFormat()
|
||||
const opt = args.length >= 3
|
||||
? args[2]
|
||||
: args.length === 1
|
||||
? dfmt
|
||||
: isObject(args[1])
|
||||
? args[1]
|
||||
: dfmt
|
||||
const dtf = new Intl.DateTimeFormat(lang, opt)
|
||||
return dtf.format(new Date(input))
|
||||
}
|
||||
|
||||
export const formatTimeAgo = (input, lang = 'en', justnow = 'just now') => {
|
||||
const t = new Date(input)
|
||||
let delta = Date.now() - t
|
||||
const tcv = getTimeConvers()
|
||||
if (delta <= tcv.second) {
|
||||
return justnow
|
||||
}
|
||||
let unit = 'second'
|
||||
for (const key in tcv) {
|
||||
if (delta < tcv[key]) {
|
||||
break
|
||||
} else {
|
||||
unit = key
|
||||
delta /= tcv[key]
|
||||
}
|
||||
}
|
||||
delta = Math.floor(delta)
|
||||
const rel = new Intl.RelativeTimeFormat(lang)
|
||||
return rel.format(-delta, unit)
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// date.test
|
||||
|
||||
/* eslint-env jest */
|
||||
|
||||
import { jest } from '@jest/globals'
|
||||
|
||||
import {
|
||||
formatDateString,
|
||||
formatTimeAgo
|
||||
} from './date.js'
|
||||
|
||||
describe('test .formatDateString() method', () => {
|
||||
const d = new Date()
|
||||
|
||||
test(' check .formatDateString() with default options', () => {
|
||||
const result = formatDateString(d)
|
||||
const reg = /^\w+\s\d+,\s+\d{4},\s\d+:\d+:\d+\s(AM|PM)\s(GMT)\+\d+$/
|
||||
expect(result.match(reg) !== null).toBeTruthy()
|
||||
})
|
||||
|
||||
test(' check .formatDateString() with custom options', () => {
|
||||
const result = formatDateString(d, {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'medium',
|
||||
hour12: true,
|
||||
})
|
||||
const reg = /^\w+,\s\w+\s\d+,\s+\d{4}\sat\s\d+:\d+:\d+\s(AM|PM)$/
|
||||
expect(result.match(reg) !== null).toBeTruthy()
|
||||
})
|
||||
|
||||
test(' check .formatDateString() with custom language and options', () => {
|
||||
const result = formatDateString(d, 'en', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'medium',
|
||||
hour12: true,
|
||||
})
|
||||
const reg = /^\w+,\s\w+\s\d+,\s+\d{4}\sat\s\d+:\d+:\d+\s(AM|PM)$/
|
||||
expect(result.match(reg) !== null).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .formatTimeAgo() method:', () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(global, 'setTimeout')
|
||||
|
||||
const d = new Date()
|
||||
|
||||
test(' check if .formatTimeAgo() return "just now"', () => {
|
||||
const result = formatTimeAgo(d)
|
||||
expect(result === 'just now').toBeTruthy()
|
||||
const justnowCustomMessage = formatTimeAgo(d, 'vi', 'vừa mới xong')
|
||||
expect(justnowCustomMessage === 'vừa mới xong').toBeTruthy()
|
||||
})
|
||||
|
||||
test(' check .formatTimeAgo() after 5s', () => {
|
||||
setTimeout(() => {
|
||||
const result = formatTimeAgo(d)
|
||||
expect(result === '5 seconds ago').toBeTruthy()
|
||||
}, 5000)
|
||||
jest.advanceTimersByTime(5000)
|
||||
})
|
||||
})
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// utils / defineProp
|
||||
|
||||
export const defineProp = (ob, key, val, config = {}) => {
|
||||
const {
|
||||
writable = false,
|
||||
configurable = false,
|
||||
enumerable = false,
|
||||
} = config
|
||||
Object.defineProperty(ob, key, {
|
||||
value: val,
|
||||
writable,
|
||||
configurable,
|
||||
enumerable,
|
||||
})
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// utils / detection
|
||||
|
||||
const ob2Str = (val) => {
|
||||
return {}.toString.call(val)
|
||||
}
|
||||
|
||||
export const isInteger = (val) => {
|
||||
return Number.isInteger(val)
|
||||
}
|
||||
|
||||
export const isArray = (val) => {
|
||||
return Array.isArray(val)
|
||||
}
|
||||
|
||||
export const isString = (val) => {
|
||||
return String(val) === val
|
||||
}
|
||||
|
||||
export const isNumber = (val) => {
|
||||
return Number(val) === val
|
||||
}
|
||||
|
||||
export const isBoolean = (val) => {
|
||||
return Boolean(val) === val
|
||||
}
|
||||
|
||||
export const isNull = (val) => {
|
||||
return ob2Str(val) === '[object Null]'
|
||||
}
|
||||
|
||||
export const isUndefined = (val) => {
|
||||
return ob2Str(val) === '[object Undefined]'
|
||||
}
|
||||
|
||||
export const isNil = (val) => {
|
||||
return isUndefined(val) || isNull(val)
|
||||
}
|
||||
|
||||
export const isFunction = (val) => {
|
||||
return ob2Str(val) === '[object Function]'
|
||||
}
|
||||
|
||||
export const isObject = (val) => {
|
||||
return ob2Str(val) === '[object Object]' && !isArray(val)
|
||||
}
|
||||
|
||||
export const isDate = (val) => {
|
||||
return val instanceof Date && !isNaN(val.valueOf())
|
||||
}
|
||||
|
||||
export const isElement = (v) => {
|
||||
return ob2Str(v).match(/^\[object HTML\w*Element]$/) !== null
|
||||
}
|
||||
|
||||
export const isLetter = (val) => {
|
||||
const re = /^[a-z]+$/i
|
||||
return isString(val) && re.test(val)
|
||||
}
|
||||
|
||||
export const isEmail = (val) => {
|
||||
const re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i
|
||||
return isString(val) && re.test(val)
|
||||
}
|
||||
|
||||
export const isEmpty = (val) => {
|
||||
return !val || isNil(val) ||
|
||||
(isString(val) && val === '') ||
|
||||
(isArray(val) && val.length === 0) ||
|
||||
(isObject(val) && Object.keys(val).length === 0)
|
||||
}
|
||||
|
||||
export const hasProperty = (ob, k) => {
|
||||
if (!ob || !k) {
|
||||
return false
|
||||
}
|
||||
return Object.prototype.hasOwnProperty.call(ob, k)
|
||||
}
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
// detection.test
|
||||
|
||||
/* eslint-env jest */
|
||||
|
||||
import {
|
||||
isInteger,
|
||||
isArray,
|
||||
isString,
|
||||
isNumber,
|
||||
isBoolean,
|
||||
isNull,
|
||||
isUndefined,
|
||||
isNil,
|
||||
isFunction,
|
||||
isObject,
|
||||
isDate,
|
||||
isElement,
|
||||
isLetter,
|
||||
isEmail,
|
||||
isEmpty,
|
||||
hasProperty
|
||||
} from './detection.js'
|
||||
|
||||
describe('test .isInteger() method:', () => {
|
||||
const positives = [1, 1000, 9999, 0, -3]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isInteger(${val}) --> true`, () => {
|
||||
expect(isInteger(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [1.5, -3.2, '', undefined]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isInteger(${val}) --> false`, () => {
|
||||
expect(isInteger(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isArray() method:', () => {
|
||||
const positives = [[], [1, 2, 3]]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isArray(${val}) --> true`, () => {
|
||||
expect(isArray(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [1.5, '', undefined]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isArray(${val}) --> false`, () => {
|
||||
expect(isArray(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isString() method:', () => {
|
||||
const positives = ['', 'abc xyz', '10000']
|
||||
positives.forEach((val) => {
|
||||
test(`test .isString(${val}) --> true`, () => {
|
||||
expect(isString(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{}, 30, [], 1.5, null, undefined]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isString(${val}) --> false`, () => {
|
||||
expect(isString(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isNumber() method:', () => {
|
||||
const positives = [1, 1.5, 0, 9999, -2]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isNumber(${val}) --> true`, () => {
|
||||
expect(isNumber(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{}, [], '', null, undefined]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isNumber(${val}) --> false`, () => {
|
||||
expect(isNumber(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isBoolean() method:', () => {
|
||||
const positives = [true, false, 3 !== 2, 3 === 2]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isBoolean(${val}) --> true`, () => {
|
||||
expect(isBoolean(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{}, [], '', 1, 0, null, undefined]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isBoolean(${val}) --> false`, () => {
|
||||
expect(isBoolean(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isNull() method:', () => {
|
||||
const positives = [null]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isNull(${val}) --> true`, () => {
|
||||
expect(isNull(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{}, [], '', 0, undefined]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isNull(${val}) --> false`, () => {
|
||||
expect(isNull(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isUndefined() method:', () => {
|
||||
let v
|
||||
const positives = [undefined, v]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isUndefined(${val}) --> true`, () => {
|
||||
expect(isUndefined(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{}, [], '', 0, null]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isUndefined(${val}) --> false`, () => {
|
||||
expect(isUndefined(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isNil() method:', () => {
|
||||
let v
|
||||
const positives = [undefined, v, null]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isNil(${val}) --> true`, () => {
|
||||
expect(isNil(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{}, [], '', 0]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isNil(${val}) --> false`, () => {
|
||||
expect(isNil(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isFunction() method:', () => {
|
||||
const positives = [function () {}, () => {}]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isFunction(${val}) --> true`, () => {
|
||||
expect(isFunction(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{}, [], '', 0, null]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isFunction(${val}) --> false`, () => {
|
||||
expect(isFunction(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isObject() method:', () => {
|
||||
const ob = new Object() // eslint-disable-line
|
||||
const positives = [{}, ob, Object.create({})]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isObject(${val}) --> true`, () => {
|
||||
expect(isObject(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [17, [], '', 0, null, () => {}, true]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isObject(${val}) --> false`, () => {
|
||||
expect(isObject(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isDate() method:', () => {
|
||||
const dt = new Date()
|
||||
const positives = [dt]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isDate(${val}) --> true`, () => {
|
||||
expect(isDate(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [17, [], '', 0, null, () => {}, true, {}, dt.toUTCString()]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isDate(${val}) --> false`, () => {
|
||||
expect(isDate(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isElement() method:', () => {
|
||||
const el = document.createElement('DIV')
|
||||
const positives = [el]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isElement(${val}) --> true`, () => {
|
||||
expect(isElement(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [17, [], '', 0, null, () => {}, true, {}]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isElement(${val}) --> false`, () => {
|
||||
expect(isElement(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isLetter() method:', () => {
|
||||
const positives = ['a', 'A', 'sigma']
|
||||
positives.forEach((val) => {
|
||||
test(`test .isLetter(${val}) --> true`, () => {
|
||||
expect(isLetter(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{}, [], '', 0, undefined, 'a23b']
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isLetter(${val}) --> false`, () => {
|
||||
expect(isLetter(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isEmail() method:', () => {
|
||||
const positives = ['admin@pwshub.com', 'abc@qtest.com']
|
||||
positives.forEach((val) => {
|
||||
test(`test .isEmail(${val}) --> true`, () => {
|
||||
expect(isEmail(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{}, [], '', 0, undefined, 'a23b@qtest@com']
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isEmail(${val}) --> false`, () => {
|
||||
expect(isEmail(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .isEmpty() method:', () => {
|
||||
const positives = ['', 0, {}, [], undefined, null]
|
||||
positives.forEach((val) => {
|
||||
test(`test .isEmpty(${val}) --> true`, () => {
|
||||
expect(isEmpty(val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{ a: 1 }, '12', 9, [7, 1]]
|
||||
negatives.forEach((val) => {
|
||||
test(`test .isEmpty(${val}) --> false`, () => {
|
||||
expect(isEmpty(val)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .hasProperty() method:', () => {
|
||||
const obj = {
|
||||
name: 'alice',
|
||||
age: 17,
|
||||
}
|
||||
const positives = ['name', 'age']
|
||||
positives.forEach((val) => {
|
||||
test(`test .hasProperty(${val}) --> true`, () => {
|
||||
expect(hasProperty(obj, val)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
const negatives = [{ a: 1 }, 'email', 9, '__proto__']
|
||||
negatives.forEach((val) => {
|
||||
test(`test .hasProperty(${val}) --> false`, () => {
|
||||
expect(hasProperty(obj, val)).toBe(false)
|
||||
})
|
||||
})
|
||||
test('test .hasProperty(null) --> false', () => {
|
||||
expect(hasProperty(null)).toBe(false)
|
||||
})
|
||||
})
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// utils / maybe
|
||||
|
||||
import {
|
||||
defineProp
|
||||
} from './defineProp.js'
|
||||
|
||||
export const maybe = (val) => {
|
||||
const __val = val
|
||||
const isNil = () => {
|
||||
return __val === null || __val === undefined
|
||||
}
|
||||
const value = () => {
|
||||
return __val
|
||||
}
|
||||
const getElse = (fn) => {
|
||||
return maybe(__val || fn())
|
||||
}
|
||||
const filter = (fn) => {
|
||||
return maybe(fn(__val) === true ? __val : null)
|
||||
}
|
||||
const map = (fn) => {
|
||||
return maybe(isNil() ? null : fn(__val))
|
||||
}
|
||||
const output = Object.create({})
|
||||
defineProp(output, '__value__', __val, { enumerable: true })
|
||||
defineProp(output, '__type__', 'Maybe', { enumerable: true })
|
||||
defineProp(output, 'isNil', isNil)
|
||||
defineProp(output, 'value', value)
|
||||
defineProp(output, 'map', map)
|
||||
defineProp(output, 'if', filter)
|
||||
defineProp(output, 'else', getElse)
|
||||
return output
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// maybe.test
|
||||
|
||||
/* eslint-env jest */
|
||||
|
||||
import {
|
||||
maybe
|
||||
} from './maybe.js'
|
||||
|
||||
describe('test .maybe() method:', () => {
|
||||
const plus5 = (x) => x + 5
|
||||
const minus2 = (x) => x - 2
|
||||
const isNumber = (x) => Number(x) === x
|
||||
const toString = (x) => 'The value is ' + String(x)
|
||||
const getDefault = () => 'This is default value'
|
||||
|
||||
test(' check if .maybe() works correctly', () => {
|
||||
const x1 = maybe(5)
|
||||
.if(isNumber)
|
||||
.map(plus5)
|
||||
.map(minus2)
|
||||
.map(toString)
|
||||
.else(getDefault)
|
||||
.value()
|
||||
expect(x1).toEqual('The value is 8')
|
||||
|
||||
const x2 = maybe('nothing')
|
||||
.if(isNumber)
|
||||
.map(plus5)
|
||||
.map(minus2)
|
||||
.map(toString)
|
||||
.else(getDefault)
|
||||
.value()
|
||||
expect(x2).toEqual('This is default value')
|
||||
})
|
||||
})
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// utils / pipe
|
||||
|
||||
export const pipe = (...fns) => {
|
||||
return fns.reduce((f, g) => (x) => g(f(x)))
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// pipe.test
|
||||
|
||||
/* eslint-env jest */
|
||||
|
||||
import {
|
||||
pipe
|
||||
} from './pipe.js'
|
||||
|
||||
describe('test .pipe() method:', () => {
|
||||
const f1 = (name) => {
|
||||
return `f1 ${name}`
|
||||
}
|
||||
const f2 = (name) => {
|
||||
return `f2 ${name}`
|
||||
}
|
||||
const f3 = (name) => {
|
||||
return `f3 ${name}`
|
||||
}
|
||||
|
||||
const addDashes = pipe(f1, f2, f3)
|
||||
|
||||
const add3 = (num) => {
|
||||
return num + 3
|
||||
}
|
||||
|
||||
const mul6 = (num) => {
|
||||
return num * 6
|
||||
}
|
||||
|
||||
const div2 = (num) => {
|
||||
return num / 2
|
||||
}
|
||||
|
||||
const sub5 = (num) => {
|
||||
return num - 5
|
||||
}
|
||||
|
||||
const calculate = pipe(add3, mul6, div2, sub5)
|
||||
|
||||
test(' check if .compose() works correctly', () => {
|
||||
expect(addDashes('Alice')).toEqual('f3 f2 f1 Alice')
|
||||
expect(calculate(5)).toEqual(19)
|
||||
})
|
||||
})
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// utils / random
|
||||
|
||||
export const randint = (min = 0, max = 1e6) => {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||
}
|
||||
|
||||
export const genid = (len = 32, prefix = '') => {
|
||||
let s = prefix
|
||||
for (let i = 0; i < len; i++) {
|
||||
const r = Math.random()
|
||||
const k = Math.floor(r * 36)
|
||||
const c = k.toString(36)
|
||||
s += (k > 9 && r > 0.3 && r < 0.7) ? c.toUpperCase() : c
|
||||
}
|
||||
return s.substring(0, len)
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// random.test
|
||||
|
||||
/* eslint-env jest */
|
||||
|
||||
import { randint, genid } from './random.js'
|
||||
|
||||
describe('test .randint() method:', () => {
|
||||
const randArr = []
|
||||
while (randArr.length < 20) {
|
||||
randArr.push(randint())
|
||||
}
|
||||
test(`test .randint() after ${randArr.length} times`, () => {
|
||||
expect(randArr).toHaveLength(20)
|
||||
const uniqVal = Array.from(new Set(randArr))
|
||||
expect(uniqVal.length).toBeGreaterThan(10)
|
||||
})
|
||||
|
||||
test('test .randint() with same min/max', () => {
|
||||
const q = randint(10, 10)
|
||||
expect(q).toEqual(10)
|
||||
})
|
||||
|
||||
const min = 50
|
||||
const max = 80
|
||||
test(`test .randint() between ${min} - ${max}`, () => {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const q = randint(min, max)
|
||||
expect(q).toBeGreaterThanOrEqual(min)
|
||||
expect(q).toBeLessThanOrEqual(max)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .genid() method:', () => {
|
||||
test('check .genid() default param', () => {
|
||||
const actual = genid()
|
||||
expect(actual).toHaveLength(32)
|
||||
})
|
||||
|
||||
test('check .genid(512)', () => {
|
||||
const actual = genid(512)
|
||||
expect(actual).toHaveLength(512)
|
||||
})
|
||||
|
||||
const len = 100
|
||||
const ids = []
|
||||
while (ids.length < len) {
|
||||
ids.push(genid())
|
||||
}
|
||||
const uniques = Array.from(new Set(ids))
|
||||
test('check .genid() always return unique string', () => {
|
||||
expect(ids).toHaveLength(len)
|
||||
expect(uniques).toHaveLength(ids.length)
|
||||
})
|
||||
})
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
// utils / string
|
||||
|
||||
import {
|
||||
isArray,
|
||||
isString,
|
||||
isNumber,
|
||||
hasProperty
|
||||
} from './detection.js'
|
||||
|
||||
const toString = (input) => {
|
||||
const s = isNumber(input) ? String(input) : input
|
||||
if (!isString(s)) {
|
||||
throw new Error('InvalidInput: String required.')
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
export const truncate = (s, len = 140) => {
|
||||
const txt = toString(s)
|
||||
const txtlen = txt.length
|
||||
if (txtlen <= len) {
|
||||
return txt
|
||||
}
|
||||
const subtxt = txt.substring(0, len).trim()
|
||||
const subtxtArr = subtxt.split(' ')
|
||||
const subtxtLen = subtxtArr.length
|
||||
if (subtxtLen > 1) {
|
||||
subtxtArr.pop()
|
||||
return subtxtArr.map(word => word.trim()).join(' ') + '...'
|
||||
}
|
||||
return subtxt.substring(0, len - 3) + '...'
|
||||
}
|
||||
|
||||
export const stripTags = (s) => {
|
||||
return toString(s).replace(/(<([^>]+)>)/ig, '').trim()
|
||||
}
|
||||
|
||||
export const escapeHTML = (s) => {
|
||||
return toString(s)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
}
|
||||
|
||||
export const unescapeHTML = (s) => {
|
||||
return toString(s)
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
}
|
||||
|
||||
export const ucfirst = (s) => {
|
||||
const x = toString(s).toLowerCase()
|
||||
return x.length > 1 ? x.charAt(0).toUpperCase() + x.slice(1) : x.toUpperCase()
|
||||
}
|
||||
|
||||
export const ucwords = (s) => {
|
||||
return toString(s).split(' ').map((w) => {
|
||||
return ucfirst(w)
|
||||
}).join(' ')
|
||||
}
|
||||
|
||||
export const replaceAll = (s, alpha, beta) => {
|
||||
let x = toString(s)
|
||||
const a = isNumber(alpha) ? String(alpha) : alpha
|
||||
const b = isNumber(beta) ? String(beta) : beta
|
||||
|
||||
if (isString(a) && isString(b)) {
|
||||
const aa = x.split(a)
|
||||
x = aa.join(b)
|
||||
} else if (isArray(a) && isString(b)) {
|
||||
a.forEach((v) => {
|
||||
x = replaceAll(x, v, b)
|
||||
})
|
||||
} else if (isArray(a) && isArray(b) && a.length === b.length) {
|
||||
const k = a.length
|
||||
if (k > 0) {
|
||||
for (let i = 0; i < k; i++) {
|
||||
const aaa = a[i]
|
||||
const bb = b[i]
|
||||
x = replaceAll(x, aaa, bb)
|
||||
}
|
||||
}
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
const getCharMap = () => {
|
||||
const lmap = {
|
||||
a: 'á|à|ả|ã|ạ|ă|ắ|ặ|ằ|ẳ|ẵ|â|ấ|ầ|ẩ|ẫ|ậ|ä|æ',
|
||||
c: 'ç',
|
||||
d: 'đ|ð',
|
||||
e: 'é|è|ẻ|ẽ|ẹ|ê|ế|ề|ể|ễ|ệ|ë',
|
||||
i: 'í|ì|ỉ|ĩ|ị|ï|î',
|
||||
n: 'ñ',
|
||||
o: 'ó|ò|ỏ|õ|ọ|ô|ố|ồ|ổ|ỗ|ộ|ơ|ớ|ờ|ở|ỡ|ợ|ö|ø',
|
||||
s: 'ß',
|
||||
u: 'ú|ù|ủ|ũ|ụ|ư|ứ|ừ|ử|ữ|ự|û',
|
||||
y: 'ý|ỳ|ỷ|ỹ|ỵ|ÿ',
|
||||
}
|
||||
|
||||
const map = {
|
||||
...lmap,
|
||||
}
|
||||
Object.keys(lmap).forEach((k) => {
|
||||
const K = k.toUpperCase()
|
||||
map[K] = lmap[k].toUpperCase()
|
||||
})
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
export const stripAccent = (s) => {
|
||||
let x = toString(s)
|
||||
|
||||
const updateS = (ai, key) => {
|
||||
x = replaceAll(x, ai, key)
|
||||
}
|
||||
|
||||
const map = getCharMap()
|
||||
for (const key in map) {
|
||||
if (hasProperty(map, key)) {
|
||||
const a = map[key].split('|')
|
||||
a.forEach((item) => {
|
||||
return updateS(item, key)
|
||||
})
|
||||
}
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
export const slugify = (s, delimiter = '-') => {
|
||||
return stripAccent(s)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/\W+/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/\s/g, delimiter)
|
||||
}
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
// string.test
|
||||
|
||||
/* eslint-env jest */
|
||||
|
||||
import {
|
||||
truncate,
|
||||
stripTags,
|
||||
escapeHTML,
|
||||
unescapeHTML,
|
||||
ucfirst,
|
||||
ucwords,
|
||||
replaceAll,
|
||||
stripAccent,
|
||||
slugify
|
||||
} from './string.js'
|
||||
|
||||
describe('test .truncate() method:', () => {
|
||||
const inputs = [
|
||||
{
|
||||
text: 'If a property is non-configurable, its writable attribute can only be changed to false.',
|
||||
limit: 60,
|
||||
expectation: 'If a property is non-configurable, its writable attribute...',
|
||||
},
|
||||
{
|
||||
text: 'this string is less than limit',
|
||||
limit: 100,
|
||||
expectation: 'this string is less than limit',
|
||||
},
|
||||
{
|
||||
text: 'uyyiyirwqyiyiyrihklhkjhskdjfhkahfiusayiyfiudyiyqwiyriuqyiouroiuyi',
|
||||
limit: 20,
|
||||
expectation: 'uyyiyirwqyiyiyrih...',
|
||||
},
|
||||
]
|
||||
|
||||
inputs.forEach(({ text, limit, expectation }, k) => {
|
||||
test(` check .truncate(text, ${k})`, () => {
|
||||
const actual = truncate(text, limit)
|
||||
expect(actual).toEqual(expectation)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .stripTags() method:', () => {
|
||||
const inputs = [
|
||||
{
|
||||
text: '<a>Hello <b>world</b></a>',
|
||||
expectation: 'Hello world',
|
||||
},
|
||||
]
|
||||
|
||||
inputs.forEach(({ text, expectation }, k) => {
|
||||
test(` check .stripTags(text, ${k})`, () => {
|
||||
const actual = stripTags(text)
|
||||
expect(actual).toEqual(expectation)
|
||||
})
|
||||
})
|
||||
|
||||
test(' check .stripTags(non-text)', () => {
|
||||
expect(() => {
|
||||
stripTags({})
|
||||
}).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .escapeHTML() method:', () => {
|
||||
const inputs = [
|
||||
{
|
||||
text: '<a>Hello <b>world</b></a>',
|
||||
expectation: '<a>Hello <b>world</b></a>',
|
||||
},
|
||||
]
|
||||
|
||||
inputs.forEach(({ text, expectation }) => {
|
||||
test(` check .escapeHTML(${text})`, () => {
|
||||
const actual = escapeHTML(text)
|
||||
expect(actual).toEqual(expectation)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .unescapeHTML() method:', () => {
|
||||
const inputs = [
|
||||
{
|
||||
text: '<a>Hello <b>world</b></a>',
|
||||
expectation: '<a>Hello <b>world</b></a>',
|
||||
},
|
||||
]
|
||||
|
||||
inputs.forEach(({ text, expectation }) => {
|
||||
test(` check .unescapeHTML(${text})`, () => {
|
||||
const actual = unescapeHTML(text)
|
||||
expect(actual).toEqual(expectation)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .ucfirst() method:', () => {
|
||||
const inputs = [
|
||||
{
|
||||
text: 'HElLo wOrLd',
|
||||
expectation: 'Hello world',
|
||||
},
|
||||
{
|
||||
text: 'h',
|
||||
expectation: 'H',
|
||||
},
|
||||
]
|
||||
|
||||
inputs.forEach(({ text, expectation }) => {
|
||||
test(` check .ucfirst(${text})`, () => {
|
||||
const actual = ucfirst(text)
|
||||
expect(actual).toEqual(expectation)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .ucwords() method:', () => {
|
||||
const inputs = [
|
||||
{
|
||||
text: 'HElLo wOrLd',
|
||||
expectation: 'Hello World',
|
||||
},
|
||||
]
|
||||
|
||||
inputs.forEach(({ text, expectation }) => {
|
||||
test(` check .ucwords(${text})`, () => {
|
||||
const actual = ucwords(text)
|
||||
expect(actual).toEqual(expectation)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .replaceAll() method:', () => {
|
||||
const inputs = [
|
||||
{
|
||||
input: {
|
||||
a: 'Hello world',
|
||||
b: 'l',
|
||||
c: '2',
|
||||
},
|
||||
expectation: 'He22o wor2d',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
a: 'Hello world',
|
||||
b: 'l',
|
||||
c: 2,
|
||||
},
|
||||
expectation: 'He22o wor2d',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
a: 798078967,
|
||||
b: 7,
|
||||
c: 1,
|
||||
},
|
||||
expectation: '198018961',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
a: 'Hello world',
|
||||
b: ['l', 'o'],
|
||||
c: ['2', '0'],
|
||||
},
|
||||
expectation: 'He220 w0r2d',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
a: 'Hello world',
|
||||
b: ['l', 'o'],
|
||||
c: '2',
|
||||
},
|
||||
expectation: 'He222 w2r2d',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
a: 'Hello world',
|
||||
b: ['l'],
|
||||
c: ['2', '0'],
|
||||
},
|
||||
expectation: 'Hello world',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
a: 'Hello world',
|
||||
b: 'l',
|
||||
},
|
||||
expectation: 'Hello world',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
a: 'Hello world',
|
||||
},
|
||||
expectation: 'Hello world',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
a: 10000,
|
||||
},
|
||||
expectation: '10000',
|
||||
},
|
||||
{
|
||||
input: {
|
||||
a: 0,
|
||||
},
|
||||
expectation: '0',
|
||||
},
|
||||
]
|
||||
|
||||
inputs.forEach(({ input, expectation }) => {
|
||||
const { a, b, c } = input
|
||||
test(` check .replaceAll(${a}, ${b}, ${c})`, () => {
|
||||
const actual = replaceAll(a, b, c)
|
||||
expect(actual).toEqual(expectation)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .stripAccent() method:', () => {
|
||||
const inputs = [
|
||||
{
|
||||
text: 'Sur l\'année 2015 - ủ Ù ỹ Ỹ',
|
||||
expectation: 'Sur l\'annee 2015 - u U y Y',
|
||||
},
|
||||
]
|
||||
|
||||
inputs.forEach(({ text, expectation }) => {
|
||||
test(` check .stripAccent(${text})`, () => {
|
||||
const actual = stripAccent(text)
|
||||
expect(actual).toEqual(expectation)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test .slugify() method:', () => {
|
||||
const inputs = [
|
||||
{
|
||||
text: 'Sur l\'année 2015',
|
||||
expectation: 'sur-l-annee-2015',
|
||||
},
|
||||
{
|
||||
text: 'Nghị luận tác phẩm "Đường kách mệnh" của Hồ Chí Minh',
|
||||
expectation: 'nghi-luan-tac-pham-duong-kach-menh-cua-ho-chi-minh',
|
||||
},
|
||||
]
|
||||
|
||||
inputs.forEach(({ text, expectation }) => {
|
||||
test(` check .slugify(${text})`, () => {
|
||||
const actual = slugify(text)
|
||||
expect(actual).toEqual(expectation)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user