Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion lib/serializer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
'use strict'

// eslint-disable-next-line
const STR_ESCAPE = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]/

module.exports = class Serializer {
constructor (options) {
switch (options && options.rounding) {
Expand Down Expand Up @@ -116,7 +119,7 @@ module.exports = class Serializer {
}
}
return (last === -1 && ('"' + str + '"')) || ('"' + result + str.slice(last) + '"')
} else if (len < 5000 && str.isWellFormed()) {
} else if (len < 5000 && STR_ESCAPE.test(str) === false) {
// Only use the regular expression for shorter input. The overhead is otherwise too much.
return '"' + str + '"'
} else {
Expand Down
107 changes: 107 additions & 0 deletions test/issue-793.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
'use strict'

const { test } = require('node:test')

const build = require('..')

test('serialize string with newlines - issue #793', (t) => {
t.plan(2)

const schema = {
type: 'object',
properties: {
message: {
type: 'string'
}
}
}

const input = {
message: `This is a string
with multiple
newlines in it
Foo`
}

const stringify = build(schema)
const output = stringify(input)

// The output should be valid JSON
t.assert.doesNotThrow(() => {
JSON.parse(output)
}, 'JSON output should be parseable')

// The parsed output should match the input
const parsed = JSON.parse(output)
t.assert.equal(parsed.message, input.message)
})

test('serialize string with various newline characters - issue #793', (t) => {
t.plan(4)

const schema = {
type: 'string'
}

const stringify = build(schema)

// Test \n (line feed)
const inputLF = 'line1\nline2'
const outputLF = stringify(inputLF)
t.assert.equal(JSON.parse(outputLF), inputLF)

// Test \r (carriage return)
const inputCR = 'line1\rline2'
const outputCR = stringify(inputCR)
t.assert.equal(JSON.parse(outputCR), inputCR)

// Test \r\n (CRLF)
const inputCRLF = 'line1\r\nline2'
const outputCRLF = stringify(inputCRLF)
t.assert.equal(JSON.parse(outputCRLF), inputCRLF)

// Test mixed newlines
const inputMixed = 'line1\nline2\rline3\r\nline4'
const outputMixed = stringify(inputMixed)
t.assert.equal(JSON.parse(outputMixed), inputMixed)
})

test('serialize object with newlines in multiple properties - issue #793', (t) => {
t.plan(2)

const schema = {
type: 'object',
properties: {
message: {
type: 'string'
},
description: {
type: 'string'
},
timestamp: {
type: 'string'
}
}
}

const input = {
message: `This is a string
with multiple
newlines in it
Foo`,
description: 'This JSON response contains a field with newline characters',
timestamp: new Date().toISOString()
}

const stringify = build(schema)
const output = stringify(input)

// The output should be valid JSON
t.assert.doesNotThrow(() => {
JSON.parse(output)
}, 'JSON output should be parseable')

// The parsed output should match the input
const parsed = JSON.parse(output)
t.assert.deepEqual(parsed, input)
})
177 changes: 177 additions & 0 deletions test/issue-794.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
'use strict'

const { test } = require('node:test')

const build = require('..')

test('serialize string with quotes - issue #794', (t) => {
t.plan(2)

const schema = {
type: 'object',
properties: {
message: {
type: 'string'
}
}
}

const input = {
message: 'Error: Property "name" is required'
}

const stringify = build(schema)
const output = stringify(input)

// The output should be valid JSON
t.assert.doesNotThrow(() => {
JSON.parse(output)
}, 'JSON output should be parseable')

// The parsed output should match the input
const parsed = JSON.parse(output)
t.assert.equal(parsed.message, input.message)
})

test('serialize string with various quote types - issue #794', (t) => {
t.plan(6)

const schema = {
type: 'string'
}

const stringify = build(schema)

// Test double quotes
const inputDoubleQuotes = 'Property "name" is required'
const outputDoubleQuotes = stringify(inputDoubleQuotes)
t.assert.doesNotThrow(() => JSON.parse(outputDoubleQuotes))
t.assert.equal(JSON.parse(outputDoubleQuotes), inputDoubleQuotes)

// Test single quotes (should be fine but test for completeness)
const inputSingleQuotes = "Property 'name' is required"
const outputSingleQuotes = stringify(inputSingleQuotes)
t.assert.doesNotThrow(() => JSON.parse(outputSingleQuotes))
t.assert.equal(JSON.parse(outputSingleQuotes), inputSingleQuotes)

// Test mixed quotes
const inputMixedQuotes = 'Error: "Property \'name\' is required"'
const outputMixedQuotes = stringify(inputMixedQuotes)
t.assert.doesNotThrow(() => JSON.parse(outputMixedQuotes))
t.assert.equal(JSON.parse(outputMixedQuotes), inputMixedQuotes)
})

test('serialize error-like object with quotes in message - issue #794', (t) => {
t.plan(2)

const schema = {
type: 'object',
properties: {
error: {
type: 'object',
properties: {
message: {
type: 'string'
},
code: {
type: 'string'
}
}
}
}
}

const input = {
error: {
message: 'Validation failed: Property "email" must be a valid email address',
code: 'VALIDATION_ERROR'
}
}

const stringify = build(schema)
const output = stringify(input)

// The output should be valid JSON
t.assert.doesNotThrow(() => {
JSON.parse(output)
}, 'JSON output should be parseable')

// The parsed output should match the input
const parsed = JSON.parse(output)
t.assert.deepEqual(parsed, input)
})

test('serialize validation errors array with quotes - issue #794', (t) => {
t.plan(2)

const schema = {
type: 'object',
properties: {
errors: {
type: 'array',
items: {
type: 'object',
properties: {
message: {
type: 'string'
},
field: {
type: 'string'
}
}
}
}
}
}

const input = {
errors: [
{
message: 'Property "name" is required',
field: 'name'
},
{
message: 'Property "email" must be a valid email address',
field: 'email'
},
{
message: 'Value must be between "1" and "100"',
field: 'age'
}
]
}

const stringify = build(schema)
const output = stringify(input)

// The output should be valid JSON
t.assert.doesNotThrow(() => {
JSON.parse(output)
}, 'JSON output should be parseable')

// The parsed output should match the input
const parsed = JSON.parse(output)
t.assert.deepEqual(parsed, input)
})

test('serialize string with backslashes and quotes - issue #794', (t) => {
t.plan(4)

const schema = {
type: 'string'
}

const stringify = build(schema)

// Test backslashes
const inputBackslash = 'Path: C:\\Users\\test\\file.json'
const outputBackslash = stringify(inputBackslash)
t.assert.doesNotThrow(() => JSON.parse(outputBackslash))
t.assert.equal(JSON.parse(outputBackslash), inputBackslash)

// Test combination of backslashes and quotes
const inputMixed = 'Error: Could not find file "C:\\Users\\test\\config.json"'
const outputMixed = stringify(inputMixed)
t.assert.doesNotThrow(() => JSON.parse(outputMixed))
t.assert.equal(JSON.parse(outputMixed), inputMixed)
})
Loading