Skip to content

Commit

Permalink
Introduce option to disable truncate behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
lo1tuma authored and gillchristian committed Jul 21, 2020
1 parent 65c2ae3 commit 7c22c1e
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 21 deletions.
77 changes: 57 additions & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,25 @@ const getValidationContext = (validation: t.ValidationError) =>
* @since 1.2.1
*/
export const TYPE_MAX_LEN = 160; // Two lines of 80-col text
const truncateType = (type: string): string =>
type.length > TYPE_MAX_LEN ? `${type.slice(0, TYPE_MAX_LEN - 3)}...` : type;
const truncateType = (type: string, options: ReporterOptions = {}): string => {
const { truncateLongTypes = true } = options;

if (truncateLongTypes && type.length > TYPE_MAX_LEN) {
return `${type.slice(0, TYPE_MAX_LEN - 3)}...`;
}

return type;
};

const errorMessageSimple = (
expectedType: string,
path: string,
error: t.ValidationError
error: t.ValidationError,
options?: ReporterOptions
) =>
// https://github.com/elm-lang/core/blob/18c9e84e975ed22649888bfad15d1efdb0128ab2/src/Native/Json.js#L199
[
`Expecting ${truncateType(expectedType)}`,
`Expecting ${truncateType(expectedType, options)}`,
path === '' ? '' : `at ${path}`,
`but instead got: ${jsToString(error.value)}`,
error.message ? `(${error.message})` : ''
Expand All @@ -75,12 +83,13 @@ const errorMessageSimple = (
const errorMessageUnion = (
expectedTypes: string[],
path: string,
value: unknown
value: unknown,
options?: ReporterOptions
) =>
// https://github.com/elm-lang/core/blob/18c9e84e975ed22649888bfad15d1efdb0128ab2/src/Native/Json.js#L199
[
'Expecting one of:\n',
expectedTypes.map(type => ` ${truncateType(type)}`).join('\n'),
expectedTypes.map(type => ` ${truncateType(type, options)}`).join('\n'),
path === '' ? '\n' : `\nat ${path} `,
`but instead got: ${jsToString(value)}`
]
Expand All @@ -98,7 +107,8 @@ const findExpectedType = (ctx: t.ContextEntry[]) =>

const formatValidationErrorOfUnion = (
path: string,
errors: NEA.NonEmptyArray<t.ValidationError>
errors: NEA.NonEmptyArray<t.ValidationError>,
options?: ReporterOptions
) => {
const expectedTypes = pipe(
errors,
Expand All @@ -117,36 +127,46 @@ const formatValidationErrorOfUnion = (
const expected = expectedTypes.map(({ type }) => type.name);

return expected.length > 0
? O.some(errorMessageUnion(expected, path, value))
? O.some(errorMessageUnion(expected, path, value, options))
: O.none;
};

const formatValidationCommonError = (path: string, error: t.ValidationError) =>
const formatValidationCommonError = (
path: string,
error: t.ValidationError,
options?: ReporterOptions
) =>
pipe(
error,
getErrorFromCtx,
O.map(errorContext =>
errorMessageSimple(errorContext.type.name, path, error)
errorMessageSimple(errorContext.type.name, path, error, options)
)
);

const groupByKey = NEA.groupBy((error: t.ValidationError) =>
pipe(error.context, takeUntil(isUnionType), keyPath)
);

const format = (path: string, errors: NEA.NonEmptyArray<t.ValidationError>) =>
const format = (
path: string,
errors: NEA.NonEmptyArray<t.ValidationError>,
options?: ReporterOptions
) =>
NEA.tail(errors).length > 0
? formatValidationErrorOfUnion(path, errors)
: formatValidationCommonError(path, NEA.head(errors));
? formatValidationErrorOfUnion(path, errors, options)
: formatValidationCommonError(path, NEA.head(errors), options);

/**
* Format a single validation error.
*
* @category formatters
* @since 1.0.0
*/
export const formatValidationError = (error: t.ValidationError) =>
formatValidationCommonError(keyPath(error.context), error);
export const formatValidationError = (
error: t.ValidationError,
options?: ReporterOptions
) => formatValidationCommonError(keyPath(error.context), error, options);

/**
* Format validation errors (`t.Errors`).
Expand All @@ -166,32 +186,49 @@ export const formatValidationError = (error: t.ValidationError) =>
* @category formatters
* @since 1.2.0
*/
export const formatValidationErrors = (errors: t.Errors) =>
export const formatValidationErrors = (
errors: t.Errors,
options?: ReporterOptions
) =>
pipe(
errors,
groupByKey,
R.mapWithIndex(format),
R.mapWithIndex((path, errors) => format(path, errors, options)),
R.compact,
R.toArray,
A.map(([_key, error]) => error)
);

export interface ReporterOptions {
truncateLongTypes?: boolean;
}

/**
* Deprecated, use the default export instead.
*
* @category deprecated
* @deprecated
* @since 1.0.0
*/
export const reporter = <T>(validation: t.Validation<T>) =>
export const reporter = <T>(
validation: t.Validation<T>,
options?: ReporterOptions
) =>
pipe(
validation,
E.mapLeft(formatValidationErrors),
E.mapLeft(errors => formatValidationErrors(errors, options)),
E.fold(
errors => errors,
() => []
)
);

const prettyReporter: Reporter<string[]> = { report: reporter };
interface PrettyReporter extends Reporter<string[]> {
report: <T>(
validation: t.Validation<T>,
options?: ReporterOptions
) => string[];
}

const prettyReporter: PrettyReporter = { report: reporter };
export default prettyReporter;
19 changes: 18 additions & 1 deletion tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ test('formats branded types correctly', t => {

const PatronizingPositive = withMessage(
Positive,
_i => `Don't be so negative!`
_i => "Don't be so negative!"
);

t.deepEqual(Reporter.report(PatronizingPositive.decode(-1)), [
Expand All @@ -78,3 +78,20 @@ test('truncates really long types', t => {
'Should be truncated'
);
});

test('doesn’t truncate really long types when truncating is disabled', t => {
const longTypeName =
'1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890';
const longType = iots.type({
[longTypeName]: iots.number
});
const messages = Reporter.report(longType.decode(null), {
truncateLongTypes: false
});
t.is(messages.length, 1);
t.is(
messages[0],
`Expecting { ${longTypeName}: number } but instead got: null`,
'Should not be truncated'
);
});

0 comments on commit 7c22c1e

Please sign in to comment.