Skip to content
Closed
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
106 changes: 106 additions & 0 deletions test/unit/shared/utils/parse-basic-frontmatter.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, expect, it } from 'vitest'
import fc from 'fast-check'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check TS module interop flags
find . -name 'tsconfig*.json' -type f | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && cat {}'

Repository: npmx-dev/npmx.dev

Length of output: 769


🏁 Script executed:

# Check existing fast-check imports
rg "from ['\"]fast-check['\"]" --type=ts --type=tsx -n

Repository: npmx-dev/npmx.dev

Length of output: 88


🏁 Script executed:

# Search for existing fast-check imports without file type restriction
rg "from ['\"]fast-check['\"]" -n

Repository: npmx-dev/npmx.dev

Length of output: 293


🏁 Script executed:

# Check if there's a test-specific tsconfig
find . -path '*/test/**' -name 'tsconfig*.json' -o -path '*/tests/**' -name 'tsconfig*.json'

Repository: npmx-dev/npmx.dev

Length of output: 43


🏁 Script executed:

# Look for vitest config and test setup
find . -name 'vitest.config.*' -o -name 'vite.config.*' | head -5

Repository: npmx-dev/npmx.dev

Length of output: 79


Use namespace import to match existing test suite convention.

The codebase consistently uses import * as fc from 'fast-check' in other test files. Align with this established pattern.

Import update
-import fc from 'fast-check'
+import * as fc from 'fast-check'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import fc from 'fast-check'
import * as fc from 'fast-check'

import { parseBasicFrontmatter } from '../../../../shared/utils/parse-basic-frontmatter'

describe('parseBasicFrontmatter', () => {
Expand Down Expand Up @@ -105,4 +106,109 @@ describe('parseBasicFrontmatter', () => {
tags: ['a', 'b'],
})
})

it.fails('handles string numerics as strings', () => {
const input = "---\nid: '42'\n---"
expect(parseBasicFrontmatter(input)).toEqual({ id: '42' })
})

it.fails('handles numbers using scientific notation', () => {
const input = '---\nprice: 1e+50\n---'
expect(parseBasicFrontmatter(input)).toEqual({ price: 1e50 })
})

it.fails('handles escaped double quote', () => {
const input = '---\nquote: "He said, \\"Hello\\""\n---'
expect(parseBasicFrontmatter(input)).toEqual({ quote: 'He said, "Hello"' })
})

it('strips leading and trailing whitespace', () => {
const input = '---\ntext: Something to say \n---'
expect(parseBasicFrontmatter(input)).toEqual({ text: 'Something to say' })
})

it.fails('handles numeric in arrays', () => {
const input = '---\ntags: [1, 2]\n---'
expect(parseBasicFrontmatter(input)).toEqual({ tags: [1, 2] })
})

it('handles strings with array like content', () => {
const input = '---\ntext: Read the content of this array [1, 2, 3]\n---'
expect(parseBasicFrontmatter(input)).toEqual({
text: 'Read the content of this array [1, 2, 3]',
})
})

it.fails('handles quoted strings with array as content', () => {
const input = '---\nvalue: "[123]"\n---'
expect(parseBasicFrontmatter(input)).toEqual({ value: '[123]' })
})

it.fails('handles string with commas when quoted in arrays', () => {
const input = '---\ntags: ["foo, bar", "baz"]\n---'
expect(parseBasicFrontmatter(input)).toEqual({ tags: ['foo, bar', 'baz'] })
})

it('should parse back every key-value pair from generated frontmatter', () => {
const keyArb = fc.stringMatching(/^[a-z]\w*$/i)
const singleValueArbs = (inArray: boolean) =>
// for arrays: all values get parsed as strings and there should not be any comma in the value even for quoted strings
[
// For boolean values
fc.boolean().map(b => ({ raw: `${b}`, expected: inArray ? `${b}` : b })),
// For number values
fc
.oneof(
// no support for -0
fc.constant(0),
// no support for scientific notation
// anything <0.000001 will be displayed in scientific notation, and anything >999999999999999900000 too
fc.double({ min: 0.000001, max: 999999999999999900000, noNaN: true }),
fc.double({ min: -999999999999999900000, max: -0.000001, noNaN: true }),
)
.map(n => ({ raw: `${n}`, expected: inArray ? `${n}` : n })),
// For string values
fc.oneof(
fc
.stringMatching(inArray ? /^[^',]*$/ : /^[^']*$/) // single-quoted string
.filter(v => Number.isNaN(Number(v))) // numbers, even quoted ones, get parsed as numbers
.map(v => ({ raw: `'${v}'`, expected: v })),
fc
.stringMatching(inArray ? /^[^",]*$/ : /^[^"]*$/) // double-quoted string
.filter(v => Number.isNaN(Number(v)))
.map(v => ({ raw: `"${v}"`, expected: v })),
fc // need to forbid [, ', " or space as first character
.stringMatching(inArray ? /^[^,]+$/ : /^.+$/) // leading and trailing whitespace are ignored for unquoted strings
.filter(v => Number.isNaN(Number(v)))
.map(v => v.trim())
.map(v => ({ raw: `'${v}'`, expected: v })),
),
]
const valueArb = fc.oneof(
...singleValueArbs(false),
fc
.array(fc.oneof(...singleValueArbs(true)), { minLength: 1 }) // all values get read as strings, no support to empty arrays
.map(arr => ({
raw: `[${arr.map(v => v.raw).join(', ')}]`,
expected: arr.map(v => v.expected),
})),
)
const frontmatterContentArb = fc.dictionary(keyArb, valueArb).map(dict => {
const entries = Object.entries(dict).map(([key, { raw, expected }]) => ({
key,
raw: `${key}: ${raw}`,
expected,
}))
return {
raw: `---\n${entries.map(e => e.raw).join('\n')}\n---\n`,
expected: Object.fromEntries(entries.map(e => [e.key, e.expected])),
}
})
fc.assert(
fc.property(frontmatterContentArb, ({ raw, expected }) => {
expect(parseBasicFrontmatter(raw)).toEqual(expected)
}),
{ numRuns: 10000, endOnFailure: true },
)
})
})
Loading