diff --git a/package.json b/package.json index 1a0ae5eb..62d8c1ad 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "typings/index.flow.js" ], "dependencies": { + "chalk": "^4.1.2", "jest-matcher-utils": "^29.7.0", "pretty-format": "^29.7.0", "redent": "^3.0.0" diff --git a/src/__tests__/render-debug.test.tsx b/src/__tests__/render-debug.test.tsx index 0b5bd462..d4ab64b2 100644 --- a/src/__tests__/render-debug.test.tsx +++ b/src/__tests__/render-debug.test.tsx @@ -1,8 +1,8 @@ -/* eslint-disable no-console */ import * as React from 'react'; import { Pressable, Text, TextInput, View } from 'react-native'; import stripAnsi from 'strip-ansi'; import { configure, fireEvent, render, screen } from '..'; +import { logger } from '../helpers/logger'; const PLACEHOLDER_FRESHNESS = 'Add custom freshness'; const PLACEHOLDER_CHEF = 'Who inspected freshness?'; @@ -13,13 +13,11 @@ const DEFAULT_INPUT_CUSTOMER = 'What banana?'; const ignoreWarnings = ['Using debug("message") is deprecated']; -const realConsoleWarn = console.warn; - beforeEach(() => { - jest.spyOn(console, 'log').mockImplementation(() => {}); - jest.spyOn(console, 'warn').mockImplementation((message) => { - if (!ignoreWarnings.some((warning) => message.includes(warning))) { - realConsoleWarn(message); + jest.spyOn(logger, 'info').mockImplementation(() => {}); + jest.spyOn(logger, 'warn').mockImplementation((message) => { + if (!ignoreWarnings.some((warning) => `${message}`.includes(warning))) { + logger.warn(message); } }); }); @@ -99,12 +97,12 @@ test('debug', () => { screen.debug('my custom message'); screen.debug({ message: 'another custom message' }); - const mockCalls = jest.mocked(console.log).mock.calls; + const mockCalls = jest.mocked(logger.info).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); expect(stripAnsi(mockCalls[1][0] + mockCalls[1][1])).toMatchSnapshot('with message'); expect(stripAnsi(mockCalls[2][0] + mockCalls[2][1])).toMatchSnapshot('another custom message'); - const mockWarnCalls = jest.mocked(console.warn).mock.calls; + const mockWarnCalls = jest.mocked(logger.warn).mock.calls; expect(mockWarnCalls[0]).toMatchInlineSnapshot(` [ "Using debug("message") is deprecated and will be removed in future release, please use debug({ message: "message" }) instead.", @@ -118,7 +116,7 @@ test('debug changing component', () => { screen.debug(); - const mockCalls = jest.mocked(console.log).mock.calls; + const mockCalls = jest.mocked(logger.info).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot( 'bananaFresh button message should now be "fresh"', ); @@ -128,7 +126,7 @@ test('debug with only children prop', () => { render(); screen.debug({ mapProps: () => ({}) }); - const mockCalls = jest.mocked(console.log).mock.calls; + const mockCalls = jest.mocked(logger.info).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); @@ -146,7 +144,7 @@ test('debug with only prop whose value is bananaChef', () => { }, }); - const mockCalls = jest.mocked(console.log).mock.calls; + const mockCalls = jest.mocked(logger.info).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); @@ -156,7 +154,7 @@ test('debug with only props from TextInput components', () => { mapProps: (props, node) => (node.type === 'TextInput' ? props : {}), }); - const mockCalls = jest.mocked(console.log).mock.calls; + const mockCalls = jest.mocked(logger.info).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); @@ -170,7 +168,7 @@ test('debug should use debugOptions from config when no option is specified', () ); screen.debug(); - const mockCalls = jest.mocked(console.log).mock.calls; + const mockCalls = jest.mocked(logger.info).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); @@ -191,6 +189,6 @@ test('debug should use given options over config debugOptions', () => { ); screen.debug({ mapProps: (props) => props }); - const mockCalls = jest.mocked(console.log).mock.calls; + const mockCalls = jest.mocked(logger.info).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); diff --git a/src/helpers/debug.ts b/src/helpers/debug.ts index 14ced11a..7088b735 100644 --- a/src/helpers/debug.ts +++ b/src/helpers/debug.ts @@ -1,5 +1,6 @@ import type { ReactTestRendererJSON } from 'react-test-renderer'; import format, { FormatOptions } from './format'; +import { logger } from './logger'; export type DebugOptions = { message?: string; @@ -17,10 +18,8 @@ export function debug( const formatOptions = typeof options === 'object' ? { mapProps: options?.mapProps } : undefined; if (message) { - // eslint-disable-next-line no-console - console.log(`${message}\n\n`, format(instance, formatOptions)); + logger.info(`${message}\n\n`, format(instance, formatOptions)); } else { - // eslint-disable-next-line no-console - console.log(format(instance, formatOptions)); + logger.info(format(instance, formatOptions)); } } diff --git a/src/helpers/logger.ts b/src/helpers/logger.ts new file mode 100644 index 00000000..94e1f53b --- /dev/null +++ b/src/helpers/logger.ts @@ -0,0 +1,32 @@ +import * as nodeConsole from 'console'; +import * as nodeUtil from 'util'; +import chalk from 'chalk'; +import redent from 'redent'; + +export const logger = { + debug(message: any, ...args: any[]) { + const output = formatMessage('●', message, ...args); + nodeConsole.debug(chalk.dim(output)); + }, + + info(message: any, ...args: any[]) { + const output = formatMessage('●', message, ...args); + nodeConsole.info(output); + }, + + warn(message: any, ...args: any[]) { + const output = formatMessage('▲', message, ...args); + nodeConsole.warn(chalk.yellow(output)); + }, + + error(message: any, ...args: any[]) { + const output = formatMessage('■', message, ...args); + nodeConsole.error(chalk.red(output)); + }, +}; + +function formatMessage(symbol: string, message: any, ...args: any[]) { + const formatted = nodeUtil.format(message, ...args); + const indented = redent(formatted, 4); + return ` ${symbol} ${indented.trimStart()}\n`; +} diff --git a/src/queries/__tests__/label-text.test.tsx b/src/queries/__tests__/label-text.test.tsx index bd554f28..e4460466 100644 --- a/src/queries/__tests__/label-text.test.tsx +++ b/src/queries/__tests__/label-text.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { View, Text, TextInput, Image, Pressable } from 'react-native'; import { render, screen } from '../..'; +import { logger } from '../../helpers/logger'; const BUTTON_LABEL = 'cool button'; const BUTTON_HINT = 'click this button'; @@ -105,7 +106,7 @@ test('getAllByLabelText, queryAllByLabelText, findAllByLabelText with exact as f describe('findBy options deprecations', () => { let warnSpy: jest.SpyInstance; beforeEach(() => { - warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + warnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); }); afterEach(() => { warnSpy.mockRestore(); diff --git a/src/queries/make-queries.ts b/src/queries/make-queries.ts index fa89b284..c6a8557a 100644 --- a/src/queries/make-queries.ts +++ b/src/queries/make-queries.ts @@ -3,6 +3,7 @@ import { ErrorWithStack } from '../helpers/errors'; import waitFor from '../wait-for'; import type { WaitForOptions } from '../wait-for'; import format from '../helpers/format'; +import { logger } from '../helpers/logger'; import { screen } from '../screen'; import { defaultMapProps } from '../helpers/format-default'; @@ -69,8 +70,7 @@ function extractDeprecatedWaitForOptions(options?: WaitForOptions) { deprecatedKeys.forEach((key) => { const option = options[key]; if (option) { - // eslint-disable-next-line no-console - console.warn( + logger.warn( `Use of option "${key}" in a findBy* query options (2nd parameter) is deprecated. Please pass this option in the waitForOptions (3rd parameter). Example: diff --git a/src/render.tsx b/src/render.tsx index dfbec315..0f35bb5b 100644 --- a/src/render.tsx +++ b/src/render.tsx @@ -10,6 +10,7 @@ import { addToCleanupQueue } from './cleanup'; import { getConfig } from './config'; import { getHostSelves } from './helpers/component-tree'; import { debug, DebugOptions } from './helpers/debug'; +import { logger } from './helpers/logger'; import { validateStringsRenderedWithinText } from './helpers/string-validation'; import { renderWithAct } from './render-act'; import { setRenderResult } from './screen'; @@ -161,8 +162,7 @@ function makeDebug(instance: ReactTestInstance, renderer: ReactTestRenderer): De : { ...defaultDebugOptions, ...options }; if (typeof options === 'string') { - // eslint-disable-next-line no-console - console.warn( + logger.warn( 'Using debug("message") is deprecated and will be removed in future release, please use debug({ message: "message" }) instead.', ); } diff --git a/yarn.lock b/yarn.lock index e8e40187..7bfae5ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2650,6 +2650,7 @@ __metadata: "@types/react-test-renderer": "npm:^18.3.0" babel-jest: "npm:^29.7.0" babel-plugin-module-resolver: "npm:^5.0.2" + chalk: "npm:^4.1.2" del-cli: "npm:^6.0.0" eslint: "npm:^8.57.1" eslint-plugin-prettier: "npm:^4.2.1" @@ -3773,7 +3774,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.0": +"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: