Skip to content

Commit

Permalink
Polish tests & code
Browse files Browse the repository at this point in the history
  • Loading branch information
dolfbarr committed Nov 28, 2022
1 parent 553c900 commit 980e70e
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 96 deletions.
51 changes: 41 additions & 10 deletions src/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,21 +170,13 @@ describe('useLog', () => {
expect(consoleLog).toBeCalledTimes(11)
expect(consoleGroup).toBeCalledTimes(6)
})

it('does not render anything in production', () => {
process.env.NODE_ENV = 'production'

const { result } = renderHook(useLog)
renderHook(() => result.current.log('Test'))
expect(consoleLog).not.toBeCalled()
})

it('renders hook with custom styles', () => {
renderHook(() => {
const { log } = useLog({ styles: { componentCSS: 'color: darkBlue;' } })
log('Test')
})

// first call, second parameter (css for component name) should be modified
expect(consoleGroup.mock.calls[0][1]).toBe('color: darkBlue;')
})

Expand All @@ -194,7 +186,37 @@ describe('useLog', () => {
log('Test', { styles: { componentCSS: 'color: darkRed;' } })
})

expect(consoleGroup.mock.calls[1][1]).toBe('color: darkRed;')
// first call, second parameter (css for component name) should be modified
expect(consoleGroup.mock.calls[0][1]).toBe('color: darkRed;')
})

it('renders log with custom styles for subValueCSS', () => {
renderHook(() => {
const { log } = useLog({ styles: { subValueCSS: 'color: darkBlue;' } })
log('Test', { styles: { subValueCSS: 'color: darkRed;' } })
})

// first call, third parameter (css for call time) should be modified
expect(consoleGroup.mock.calls[0][2]).toBe('color: darkRed;')
})

it('renders log with custom styles for changeCSS', () => {
renderHook(() => {
const { log } = useLog({ styles: { changeCSS: 'color: darkBlue;' } })
log('Test', { styles: { changeCSS: 'color: darkRed;' } })
})

// third call, third parameter (css for new value) should be modified
expect(consoleLog.mock.calls[2][1]).toBe('color: darkRed;')
})

it('does not render anything in production', () => {
process.env.NODE_ENV = 'production'

const { result } = renderHook(useLog)
renderHook(() => result.current.log('Test'))

expect(consoleLog).not.toBeCalled()
})

it('renders anything in custom allowed environments', () => {
Expand All @@ -207,4 +229,13 @@ describe('useLog', () => {

expect(consoleLog).toBeCalled()
})

it('falls back to production for empty node_env', () => {
process.env.NODE_ENV = undefined

const { result } = renderHook(useLog)
renderHook(() => result.current.log('Test'))

expect(consoleLog).not.toBeCalled()
})
})
109 changes: 23 additions & 86 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,13 @@
import { useEffect, useRef } from 'react'
import { UseLog, UseLogReturn, Log, PrintTypes, PrintProps } from './types'
import { getComponentName, print } from './utils'

const CSS_COMPONENT = 'color: DodgerBlue'
const CSS_CHANGE = 'color: green; font-weight: bold;'
const CSS_SUB_VALUE = 'color: SlateGray; font-weight: thin;'

const ALLOWED_NODE_ENVS = ['dev', 'development']

export interface UseLog {
styles?: {
componentCSS?: string
changeCSS?: string
subValueCSS?: string
}
environments?: string[]
}

export type Log = UseLog

export interface UseLogReturn {
log: <T>(value: T, props?: Log) => void
}

interface PrintProps<T> {
value: T
prevValue?: T
label: string
group?: string
type?: PrintTypes
}

export enum PrintTypes {
Mount = 'Mount',
Unmount = 'Unmount',
Change = 'Change',
}

export function useLog({
styles: {
componentCSS = CSS_COMPONENT,
Expand All @@ -43,64 +16,26 @@ export function useLog({
} = {},
environments = ALLOWED_NODE_ENVS,
}: UseLog = {}): UseLogReturn {
const componentName =
(function getComponentName() {
try {
throw new Error()
} catch (error) {
if (error instanceof Error) {
const re = /(\w+)@|at (\w+) \(/g

re.exec(error?.stack ?? '')
re.exec(error?.stack ?? '')
const m = re.exec(error?.stack ?? '') ?? []

return String(m[1] || m[2])
}
}
})() ?? ''

function getGroupLabel(type: PrintTypes): string {
return `${String(type)} ${
componentName ? 'in %c<' + String(componentName) + ' /> ' : '%c'
}%c@ ${new Date().toLocaleTimeString()}`
}
const componentName = getComponentName()

function log<T>(value: T, props?: Log): void {
const clonedValue = JSON.parse(JSON.stringify(value))
const clonedValue = JSON.parse(JSON.stringify(value)) as T
const prevValueRef = useRef<T>()

function print<T>({
value,
label,
prevValue,
type = PrintTypes.Change,
group = getGroupLabel(type),
}: PrintProps<T>): void {
console.group(
group,
props?.styles?.componentCSS ?? componentCSS,
props?.styles?.subValueCSS ?? subValueCSS,
)

if (!('prevValue' in arguments[0])) {
console.log(`${label.padStart(14, ' ')}: ${String(value)}`)
} else {
console.log(
`Previous value: %c${String(arguments[0].prevValue)}`,
props?.styles?.subValueCSS ?? subValueCSS,
)
console.log(
` Current value: %c${String(value)}`,
props?.styles?.changeCSS ?? changeCSS,
)
}

console.groupEnd()
const printProps: Pick<
PrintProps<T>,
'value' | 'styles' | 'componentName'
> = {
value: clonedValue,
styles: {
componentCSS: props?.styles?.componentCSS ?? componentCSS,
subValueCSS: props?.styles?.subValueCSS ?? subValueCSS,
changeCSS: props?.styles?.changeCSS ?? changeCSS,
},
componentName,
}

if (environments.includes(process.env.NODE_ENV ?? '')) {
return (function logHooks() {
if (environments.includes(process.env.NODE_ENV ?? 'production')) {
function logHooks(): void {
const isUnmounting = useRef(false)
useEffect(function setIsUnmounting() {
return function setIsUnmountingOnMount() {
Expand All @@ -111,18 +46,18 @@ export function useLog({
useEffect(function onMount() {
print({
label: 'On mount',
value: clonedValue,
type: PrintTypes.Mount,
...printProps,
})

prevValueRef.current = value

return function onUnmount() {
print({
label: 'On unmount',
value: clonedValue,
type: PrintTypes.Unmount,
prevValue: prevValueRef.current,
...printProps,
})
}
}, [])
Expand All @@ -131,16 +66,18 @@ export function useLog({
function onChange() {
print({
label: 'On change',
value: clonedValue,
type: PrintTypes.Change,
prevValue: prevValueRef.current,
...printProps,
})

prevValueRef.current = value
},
[value],
)
})()
}

return logHooks()
}
}

Expand Down
32 changes: 32 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export interface Styles {
componentCSS?: string
changeCSS?: string
subValueCSS?: string
}

export interface UseLog {
styles?: Styles
environments?: string[]
}

export type Log = UseLog

export interface UseLogReturn {
log: <T>(value: T, props?: Log) => void
}

export interface PrintProps<T> {
value: T
prevValue?: T
label: string
group?: string
type?: PrintTypes
styles?: Styles
componentName: string
}

export enum PrintTypes {
Mount = 'Mount',
Unmount = 'Unmount',
Change = 'Change',
}
73 changes: 73 additions & 0 deletions src/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { getGroupLabel, getComponentName, print } from './utils'
import { PrintProps, PrintTypes } from './types'

describe('utils', () => {
describe('getGroupLabel', () => {
it('renders', () => {
expect(getGroupLabel(PrintTypes.Change)).toEqual(
`Change %c%c@ ${new Date().toLocaleTimeString()}`,
)
})

it('renders with component name', () => {
expect(getGroupLabel(PrintTypes.Mount, 'TestComponent')).toEqual(
`Mount in %c<TestComponent /> %c@ ${new Date().toLocaleTimeString()}`,
)
})
})

describe('getComponentName', () => {
it('gets component name', () => {
expect(getComponentName()).toEqual('_callCircusTest')
})
})

describe('print', () => {
const consoleLog = jest.spyOn(console, 'log').mockImplementation(() => null)
const consoleGroup = jest
.spyOn(console, 'group')
.mockImplementation(() => null)
const consoleGroupEnd = jest
.spyOn(console, 'groupEnd')
.mockImplementation(() => null)

const printProps: PrintProps<string> = {
value: 'Test Value',
label: 'A Label',
componentName: 'SomeComponentName',
}

it('prints', () => {
print(printProps)

expect(consoleGroup).toHaveBeenCalledWith(
`Change in %c<SomeComponentName /> %c@ ${new Date().toLocaleTimeString()}`,
undefined,
undefined,
)
expect(consoleLog).toHaveBeenCalledWith(' A Label: Test Value')
expect(consoleLog).toHaveBeenCalledTimes(1)
expect(consoleGroupEnd).toHaveBeenCalled()
})

it('prints previous value', () => {
print({ ...printProps, prevValue: 'Some Previous value' })

expect(consoleGroup).toHaveBeenCalledWith(
`Change in %c<SomeComponentName /> %c@ ${new Date().toLocaleTimeString()}`,
undefined,
undefined,
)
expect(consoleLog).toHaveBeenCalledWith(
'Previous value: %cSome Previous value',
undefined,
)
expect(consoleLog).toHaveBeenCalledWith(
' Current value: %cTest Value',
undefined,
)
expect(consoleLog).toHaveBeenCalledTimes(2)
expect(consoleGroupEnd).toHaveBeenCalled()
})
})
})
Loading

0 comments on commit 980e70e

Please sign in to comment.