diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index fb9a97d..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -* text=auto -*.* text eol=lf diff --git a/README.md b/README.md index 77968dd..26f93e0 100644 --- a/README.md +++ b/README.md @@ -179,14 +179,11 @@ export interface DirEnt { ## Note regarding Windows -We're relying on text files and want to have byte-perfect representations of test fixtures, but this presents some problems for Windows. By default (unless the user has changed the settings), git on Windows will convert line endings to Windows style which include a carriage return (`\r`) character. This isn't good, because we don't know whether these characters are part of our fixture data or not! +Ideally when we're relying on text files for test data input we'd want to have byte-perfect representations of fixtures, but this presents some problems for Windows. By default (unless the user has changed the settings), git on Windows will convert line endings to Windows style which include a carriage return (`\r`) character. This isn't good, because we don't know whether these characters are part of our fixture data or not! -Our recommendation if you expect to support Windows users, or have Windows users try and run your tests, is to add a `.gitattributes` file to your Git project that uses testmark with something like the following: +So, testmark takes a rather brute-force approach to this problem and just strips out carriage return characters when they appear with a line-ending. In practice this _may_ impact the byte-perfect requirements for test fixtures, so you should be careful when using data that strays outside of standard printable character range, especially when control characters get involved. This is a text file format, if your data isn't text, then make it text by encoding in hex or base64 or something that reduces the character set to the safe range. -``` -* text=auto -*.* text eol=lf -``` +The `Document#original` property and `toString()` function are going to result in text that only uses UNIX line endings (`\n`). So even your input document will be transformed if it does a round-trip through testmark on Windows. Be warned! ## Note about the package name diff --git a/parse.js b/parse.js index d187256..a8d97fb 100644 --- a/parse.js +++ b/parse.js @@ -14,10 +14,13 @@ export function parse (original) { throw new TypeError('Expected a Markdown document string') } + // sorry windows users, we're even turning your original to unix + original = original.replace(/\r?\n/g, '\n') + /** @type {Document & {original:string}} */ const doc = { original, - lines: original.split('\n'), // can't split with \r? because we need offsets + lines: original.split('\n'), dataHunks: /** @type {DocHunk[]} */ ([]), hunksByName: new Map() } diff --git a/test/test-parse.js b/test/test-parse.js index 611e26c..80e14ed 100644 --- a/test/test-parse.js +++ b/test/test-parse.js @@ -32,11 +32,33 @@ const exampleMdExpectedHunks = [ ] describe('Read', () => { - it('can parse example.md', async () => { + /** @type {string} */ + let exampleMdOriginal + let isWindows = false + + before(async () => { const exampleMd = new URL('../example.md', import.meta.url) - const exampleMdOriginal = await fs.promises.readFile(exampleMd, 'utf8') + exampleMdOriginal = await fs.promises.readFile(exampleMd, 'utf8') + isWindows = exampleMdOriginal.includes('\r\n') + }) + + it('can parse example.md', async () => { const doc = parse(exampleMdOriginal) assert.deepStrictEqual(exampleMdExpectedHunks, doc.dataHunks) + if (isWindows) { + assert.deepStrictEqual(toString(doc).replace(/\n/g, '\r\n'), exampleMdOriginal) + } else { + assert.deepStrictEqual(toString(doc), exampleMdOriginal) + } + }) + + it('can parse example.md as windows', async function () { + if (isWindows) { // skip test on windows + return this.skip() + } + const exampleMdOriginalWindows = exampleMdOriginal.replace(/\r?\n/g, '\r\n') + const doc = parse(exampleMdOriginalWindows) + assert.deepStrictEqual(exampleMdExpectedHunks, doc.dataHunks) assert.deepStrictEqual(toString(doc), exampleMdOriginal) }) }) diff --git a/test/test-patch.js b/test/test-patch.js index 8efbc85..d420cb3 100644 --- a/test/test-patch.js +++ b/test/test-patch.js @@ -10,6 +10,7 @@ describe('Patch', () => { it('can patch example.md', async () => { const exampleMd = new URL('../example.md', import.meta.url) const exampleMdOriginal = await fs.promises.readFile(exampleMd, 'utf8') + const isWindows = exampleMdOriginal.includes('\r\n') const patchedMd = new URL('./patch_expected.md', import.meta.url) // extracted from patch_test.go const patchedMdContents = await fs.promises.readFile(patchedMd, 'utf8') @@ -20,6 +21,10 @@ describe('Patch', () => { { name: 'this-one-is-new', blockTag: 'json', body: '{"hayo": "new data!"}' }, { name: 'so-is-this', blockTag: 'json', body: '{"appending": "is fun"}' } ]) - assert.strictEqual(newDoc.original, patchedMdContents) + if (isWindows) { + assert.strictEqual(newDoc.original.replace(/\n/g, '\r\n'), patchedMdContents) + } else { + assert.strictEqual(newDoc.original, patchedMdContents) + } }) }) diff --git a/types/parse.d.ts.map b/types/parse.d.ts.map index f54b1e6..41696ed 100644 --- a/types/parse.d.ts.map +++ b/types/parse.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../parse.js"],"names":[],"mappings":"AAEA;;;GAGG;AAEH;;;GAGG;AACH,gCAHW,MAAM,GACJ,QAAQ,CA2EpB;sBAjFY,OAAO,aAAa,EAAE,OAAO;uBAC7B,OAAO,aAAa,EAAE,QAAQ"} \ No newline at end of file +{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../parse.js"],"names":[],"mappings":"AAEA;;;GAGG;AAEH;;;GAGG;AACH,gCAHW,MAAM,GACJ,QAAQ,CA8EpB;sBApFY,OAAO,aAAa,EAAE,OAAO;uBAC7B,OAAO,aAAa,EAAE,QAAQ"} \ No newline at end of file