From 07dcfe99237ecb2b616c13d9885a2d4c277cd301 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Fri, 22 Jul 2022 16:38:54 -0500 Subject: [PATCH 1/8] refactor: make `pushDoc` more generic ... so we can reuse it for Changelog! :rocket: No functional changes were made, docs tests should still pass. --- src/cmds/docs/index.js | 2 +- src/cmds/docs/single.js | 2 +- src/lib/pushDoc.js | 26 +++++++++++++------------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cmds/docs/index.js b/src/cmds/docs/index.js index 66f20616b..da34169d9 100644 --- a/src/cmds/docs/index.js +++ b/src/cmds/docs/index.js @@ -85,7 +85,7 @@ module.exports = class DocsCommand { const updatedDocs = await Promise.all( files.map(async filename => { - return pushDoc(key, selectedVersion, dryRun, filename); + return pushDoc(key, selectedVersion, dryRun, filename, this.category); }) ); diff --git a/src/cmds/docs/single.js b/src/cmds/docs/single.js index a4747eb46..c8a6998ac 100644 --- a/src/cmds/docs/single.js +++ b/src/cmds/docs/single.js @@ -62,7 +62,7 @@ module.exports = class SingleDocCommand { debug(`selectedVersion: ${selectedVersion}`); - const createdDoc = await pushDoc(key, selectedVersion, dryRun, filepath); + const createdDoc = await pushDoc(key, selectedVersion, dryRun, filepath, this.category); return chalk.green(createdDoc); } diff --git a/src/lib/pushDoc.js b/src/lib/pushDoc.js index 7d309ea78..d64f5cd07 100644 --- a/src/lib/pushDoc.js +++ b/src/lib/pushDoc.js @@ -18,10 +18,11 @@ const { debug } = require('./logger'); * @param {String} selectedVersion the project version * @param {Boolean} dryRun boolean indicating dry run mode * @param {String} filepath path to the Markdown file + * @param {String} type module within ReadMe to update (e.g. docs, changelogs, etc.) * (file extension must end in `.md` or `.markdown`) * @returns {Promise} a string containing the result */ -module.exports = async function pushDoc(key, selectedVersion, dryRun, filepath) { +module.exports = async function pushDoc(key, selectedVersion, dryRun, filepath, type) { debug(`reading file ${filepath}`); const file = fs.readFileSync(filepath, 'utf8'); const matter = grayMatter(file); @@ -31,16 +32,14 @@ module.exports = async function pushDoc(key, selectedVersion, dryRun, filepath) const slug = matter.data.slug || path.basename(filepath).replace(path.extname(filepath), '').toLowerCase(); const hash = crypto.createHash('sha1').update(file).digest('hex'); - function createDoc(err) { - if (err.error !== 'DOC_NOTFOUND') return Promise.reject(new APIError(err)); - + function createDoc() { if (dryRun) { return `🎭 dry run! This will create '${slug}' with contents from ${filepath} with the following metadata: ${JSON.stringify( matter.data )}`; } - return fetch(`${config.get('host')}/api/v1/docs`, { + return fetch(`${config.get('host')}/api/v1/${type}`, { method: 'post', headers: cleanHeaders(key, { 'x-readme-version': selectedVersion, @@ -70,7 +69,7 @@ module.exports = async function pushDoc(key, selectedVersion, dryRun, filepath) )}`; } - return fetch(`${config.get('host')}/api/v1/docs/${slug}`, { + return fetch(`${config.get('host')}/api/v1/${type}/${slug}`, { method: 'put', headers: cleanHeaders(key, { 'x-readme-version': selectedVersion, @@ -88,22 +87,23 @@ module.exports = async function pushDoc(key, selectedVersion, dryRun, filepath) .then(res => `✏️ successfully updated '${res.slug}' with contents from ${filepath}`); } - return fetch(`${config.get('host')}/api/v1/docs/${slug}`, { + return fetch(`${config.get('host')}/api/v1/${type}/${slug}`, { method: 'get', headers: cleanHeaders(key, { 'x-readme-version': selectedVersion, Accept: 'application/json', }), }) - .then(res => res.json()) - .then(res => { - debug(`GET /docs/:slug API response for ${slug}: ${JSON.stringify(res)}`); - if (res.error) { + .then(async res => { + const body = await res.json(); + debug(`GET /${type}/:slug API response for ${slug}: ${JSON.stringify(body)}`); + if (!res.ok) { + if (res.status !== 404) return Promise.reject(new APIError(body)); debug(`error retrieving data for ${slug}, creating doc`); - return createDoc(res); + return createDoc(body); } debug(`data received for ${slug}, updating doc`); - return updateDoc(res); + return updateDoc(body); }) .catch(err => { // eslint-disable-next-line no-param-reassign From a4eee54a6223ee846fd2f1b8176a073bc370ad6f Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Fri, 22 Jul 2022 16:42:58 -0500 Subject: [PATCH 2/8] chore: camelcase filePath, grammar tweaks --- __tests__/cmds/docs.test.js | 34 +++++++++++++++++----------------- src/cmds/docs/single.js | 18 +++++++++--------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/__tests__/cmds/docs.test.js b/__tests__/cmds/docs.test.js index f25e78d37..ace0b6e93 100644 --- a/__tests__/cmds/docs.test.js +++ b/__tests__/cmds/docs.test.js @@ -554,15 +554,15 @@ describe('rdme docs:single', () => { return expect(docsSingle.run({})).rejects.toThrow('No project API key provided. Please use `--key`.'); }); - it('should error if no filepath provided', () => { + it('should error if no file path provided', () => { return expect(docsSingle.run({ key, version: '1.0.0' })).rejects.toThrow( - 'No filepath provided. Usage `rdme docs:single [options]`.' + 'No file path provided. Usage `rdme docs:single [options]`.' ); }); it('should error if the argument is not a markdown file', async () => { - await expect(docsSingle.run({ key, version: '1.0.0', filepath: 'not-a-markdown-file' })).rejects.toThrow( - 'The filepath specified is not a markdown file.' + await expect(docsSingle.run({ key, version: '1.0.0', filePath: 'not-a-markdown-file' })).rejects.toThrow( + 'The file path specified is not a markdown file.' ); }); @@ -593,7 +593,7 @@ describe('rdme docs:single', () => { .reply(200, { version }); await expect( - docsSingle.run({ filepath: './__tests__/__fixtures__/new-docs/new-doc.md', key, version }) + docsSingle.run({ filePath: './__tests__/__fixtures__/new-docs/new-doc.md', key, version }) ).resolves.toBe( "🌱 successfully created 'new-doc' with contents from ./__tests__/__fixtures__/new-docs/new-doc.md" ); @@ -623,7 +623,7 @@ describe('rdme docs:single', () => { .reply(200, { version }); await expect( - docsSingle.run({ dryRun: true, filepath: './__tests__/__fixtures__/new-docs/new-doc.md', key, version }) + docsSingle.run({ dryRun: true, filePath: './__tests__/__fixtures__/new-docs/new-doc.md', key, version }) ).resolves.toBe( `🎭 dry run! This will create 'new-doc' with contents from ./__tests__/__fixtures__/new-docs/new-doc.md with the following metadata: ${JSON.stringify( doc.data @@ -667,14 +667,14 @@ describe('rdme docs:single', () => { .basicAuth({ user: key }) .reply(200, { version }); - const filepath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; + const filePath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; const formattedErrorObject = { ...errorObject, - message: `Error uploading ${chalk.underline(`${filepath}`)}:\n\n${errorObject.message}`, + message: `Error uploading ${chalk.underline(`${filePath}`)}:\n\n${errorObject.message}`, }; - await expect(docsSingle.run({ filepath: `${filepath}`, key, version })).rejects.toStrictEqual( + await expect(docsSingle.run({ filePath: `${filePath}`, key, version })).rejects.toStrictEqual( new APIError(formattedErrorObject) ); @@ -703,14 +703,14 @@ describe('rdme docs:single', () => { .basicAuth({ user: key }) .reply(200, { version }); - const filepath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; + const filePath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; const formattedErrorObject = { ...errorObject, - message: `Error uploading ${chalk.underline(`${filepath}`)}:\n\n${errorObject.message}`, + message: `Error uploading ${chalk.underline(`${filePath}`)}:\n\n${errorObject.message}`, }; - await expect(docsSingle.run({ filepath: `${filepath}`, key, version })).rejects.toStrictEqual( + await expect(docsSingle.run({ filePath: `${filePath}`, key, version })).rejects.toStrictEqual( new APIError(formattedErrorObject) ); @@ -746,7 +746,7 @@ describe('rdme docs:single', () => { .reply(200, { version }); await expect( - docsSingle.run({ filepath: './__tests__/__fixtures__/slug-docs/new-doc-slug.md', key, version }) + docsSingle.run({ filePath: './__tests__/__fixtures__/slug-docs/new-doc-slug.md', key, version }) ).resolves.toBe( "🌱 successfully created 'marc-actually-wrote-a-test' with contents from ./__tests__/__fixtures__/slug-docs/new-doc-slug.md" ); @@ -796,7 +796,7 @@ describe('rdme docs:single', () => { .reply(200, { version }); return docsSingle - .run({ filepath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) + .run({ filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) .then(updatedDocs => { expect(updatedDocs).toBe( "✏️ successfully updated 'simple-doc' with contents from ./__tests__/__fixtures__/existing-docs/simple-doc.md" @@ -822,7 +822,7 @@ describe('rdme docs:single', () => { .reply(200, { version }); return docsSingle - .run({ dryRun: true, filepath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) + .run({ dryRun: true, filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) .then(updatedDocs => { // All docs should have been updated because their hashes from the GET request were different from what they // are currently. @@ -853,7 +853,7 @@ describe('rdme docs:single', () => { .reply(200, { version }); return docsSingle - .run({ filepath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) + .run({ filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) .then(skippedDocs => { expect(skippedDocs).toBe('`simple-doc` was not updated because there were no changes.'); @@ -874,7 +874,7 @@ describe('rdme docs:single', () => { .reply(200, { version }); return docsSingle - .run({ dryRun: true, filepath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) + .run({ dryRun: true, filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) .then(skippedDocs => { expect(skippedDocs).toBe('🎭 dry run! `simple-doc` will not be updated because there were no changes.'); diff --git a/src/cmds/docs/single.js b/src/cmds/docs/single.js index c8a6998ac..b41d94ee5 100644 --- a/src/cmds/docs/single.js +++ b/src/cmds/docs/single.js @@ -8,12 +8,12 @@ const pushDoc = require('../../lib/pushDoc'); module.exports = class SingleDocCommand { constructor() { this.command = 'docs:single'; - this.usage = 'docs:single [options]'; + this.usage = 'docs:single [options]'; this.description = 'Sync a single Markdown file to your ReadMe project.'; this.category = 'docs'; this.position = 3; - this.hiddenArgs = ['filepath']; + this.hiddenArgs = ['filePath']; this.args = [ { name: 'key', @@ -26,7 +26,7 @@ module.exports = class SingleDocCommand { description: 'Project version', }, { - name: 'filepath', + name: 'filePath', type: String, defaultOption: true, }, @@ -39,7 +39,7 @@ module.exports = class SingleDocCommand { } async run(opts) { - const { dryRun, filepath, key, version } = opts; + const { dryRun, filePath, key, version } = opts; debug(`command: ${this.command}`); debug(`opts: ${JSON.stringify(opts)}`); @@ -47,12 +47,12 @@ module.exports = class SingleDocCommand { return Promise.reject(new Error('No project API key provided. Please use `--key`.')); } - if (!filepath) { - return Promise.reject(new Error(`No filepath provided. Usage \`${config.get('cli')} ${this.usage}\`.`)); + if (!filePath) { + return Promise.reject(new Error(`No file path provided. Usage \`${config.get('cli')} ${this.usage}\`.`)); } - if (filepath.endsWith('.md') === false || !filepath.endsWith('.markdown') === false) { - return Promise.reject(new Error('The filepath specified is not a markdown file.')); + if (filePath.endsWith('.md') === false || !filePath.endsWith('.markdown') === false) { + return Promise.reject(new Error('The file path specified is not a markdown file.')); } // TODO: should we allow version selection at all here? @@ -62,7 +62,7 @@ module.exports = class SingleDocCommand { debug(`selectedVersion: ${selectedVersion}`); - const createdDoc = await pushDoc(key, selectedVersion, dryRun, filepath, this.category); + const createdDoc = await pushDoc(key, selectedVersion, dryRun, filePath, this.category); return chalk.green(createdDoc); } From 98fbebde64289cf7f796a2db2e90551f05e0ec5c Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 25 Jul 2022 18:32:35 -0500 Subject: [PATCH 3/8] test: split out fixtures --- .../existing-docs/not-a-markdown-file | 0 .../{ => docs}/existing-docs/simple-doc.md | 0 .../existing-docs/subdir/another-doc.md | 0 .../{ => docs}/failure-docs/fail-doc.md | 0 .../{ => docs}/failure-docs/new-doc.md | 0 .../{ => docs}/new-docs/new-doc.md | 0 .../{ => docs}/slug-docs/new-doc-slug.md | 0 __tests__/cmds/docs.test.js | 106 +++++++++--------- 8 files changed, 55 insertions(+), 51 deletions(-) rename __tests__/__fixtures__/{ => docs}/existing-docs/not-a-markdown-file (100%) rename __tests__/__fixtures__/{ => docs}/existing-docs/simple-doc.md (100%) rename __tests__/__fixtures__/{ => docs}/existing-docs/subdir/another-doc.md (100%) rename __tests__/__fixtures__/{ => docs}/failure-docs/fail-doc.md (100%) rename __tests__/__fixtures__/{ => docs}/failure-docs/new-doc.md (100%) rename __tests__/__fixtures__/{ => docs}/new-docs/new-doc.md (100%) rename __tests__/__fixtures__/{ => docs}/slug-docs/new-doc-slug.md (100%) diff --git a/__tests__/__fixtures__/existing-docs/not-a-markdown-file b/__tests__/__fixtures__/docs/existing-docs/not-a-markdown-file similarity index 100% rename from __tests__/__fixtures__/existing-docs/not-a-markdown-file rename to __tests__/__fixtures__/docs/existing-docs/not-a-markdown-file diff --git a/__tests__/__fixtures__/existing-docs/simple-doc.md b/__tests__/__fixtures__/docs/existing-docs/simple-doc.md similarity index 100% rename from __tests__/__fixtures__/existing-docs/simple-doc.md rename to __tests__/__fixtures__/docs/existing-docs/simple-doc.md diff --git a/__tests__/__fixtures__/existing-docs/subdir/another-doc.md b/__tests__/__fixtures__/docs/existing-docs/subdir/another-doc.md similarity index 100% rename from __tests__/__fixtures__/existing-docs/subdir/another-doc.md rename to __tests__/__fixtures__/docs/existing-docs/subdir/another-doc.md diff --git a/__tests__/__fixtures__/failure-docs/fail-doc.md b/__tests__/__fixtures__/docs/failure-docs/fail-doc.md similarity index 100% rename from __tests__/__fixtures__/failure-docs/fail-doc.md rename to __tests__/__fixtures__/docs/failure-docs/fail-doc.md diff --git a/__tests__/__fixtures__/failure-docs/new-doc.md b/__tests__/__fixtures__/docs/failure-docs/new-doc.md similarity index 100% rename from __tests__/__fixtures__/failure-docs/new-doc.md rename to __tests__/__fixtures__/docs/failure-docs/new-doc.md diff --git a/__tests__/__fixtures__/new-docs/new-doc.md b/__tests__/__fixtures__/docs/new-docs/new-doc.md similarity index 100% rename from __tests__/__fixtures__/new-docs/new-doc.md rename to __tests__/__fixtures__/docs/new-docs/new-doc.md diff --git a/__tests__/__fixtures__/slug-docs/new-doc-slug.md b/__tests__/__fixtures__/docs/slug-docs/new-doc-slug.md similarity index 100% rename from __tests__/__fixtures__/slug-docs/new-doc-slug.md rename to __tests__/__fixtures__/docs/slug-docs/new-doc-slug.md diff --git a/__tests__/cmds/docs.test.js b/__tests__/cmds/docs.test.js index ace0b6e93..71514d3f7 100644 --- a/__tests__/cmds/docs.test.js +++ b/__tests__/cmds/docs.test.js @@ -16,7 +16,9 @@ const docs = new DocsCommand(); const docsEdit = new DocsEditCommand(); const docsSingle = new DocsSingleCommand(); -const fixturesDir = `${__dirname}./../__fixtures__`; +const fixturesBaseDir = '__fixtures__/docs'; +const fullFixturesDir = `${__dirname}./../${fixturesBaseDir}`; + const key = 'API_KEY'; const version = '1.0.0'; const category = 'CATEGORY_ID'; @@ -72,14 +74,14 @@ describe('rdme docs', () => { let anotherDoc; beforeEach(() => { - let fileContents = fs.readFileSync(path.join(fixturesDir, '/existing-docs/simple-doc.md')); + let fileContents = fs.readFileSync(path.join(fullFixturesDir, '/existing-docs/simple-doc.md')); simpleDoc = { slug: 'simple-doc', doc: frontMatter(fileContents), hash: hashFileContents(fileContents), }; - fileContents = fs.readFileSync(path.join(fixturesDir, '/existing-docs/subdir/another-doc.md')); + fileContents = fs.readFileSync(path.join(fullFixturesDir, '/existing-docs/subdir/another-doc.md')); anotherDoc = { slug: 'another-doc', doc: frontMatter(fileContents), @@ -127,13 +129,13 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(200, { version }); - return docs.run({ folder: './__tests__/__fixtures__/existing-docs', key, version }).then(updatedDocs => { + return docs.run({ folder: `./__tests__/${fixturesBaseDir}/existing-docs`, key, version }).then(updatedDocs => { // All docs should have been updated because their hashes from the GET request were different from what they // are currently. expect(updatedDocs).toBe( [ - "✏️ successfully updated 'simple-doc' with contents from __tests__/__fixtures__/existing-docs/simple-doc.md", - "✏️ successfully updated 'another-doc' with contents from __tests__/__fixtures__/existing-docs/subdir/another-doc.md", + `✏️ successfully updated 'simple-doc' with contents from __tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, + `✏️ successfully updated 'another-doc' with contents from __tests__/${fixturesBaseDir}/existing-docs/subdir/another-doc.md`, ].join('\n') ); @@ -160,16 +162,16 @@ describe('rdme docs', () => { .reply(200, { version }); return docs - .run({ dryRun: true, folder: './__tests__/__fixtures__/existing-docs', key, version }) + .run({ dryRun: true, folder: `./__tests__/${fixturesBaseDir}/existing-docs`, key, version }) .then(updatedDocs => { // All docs should have been updated because their hashes from the GET request were different from what they // are currently. expect(updatedDocs).toBe( [ - `🎭 dry run! This will update 'simple-doc' with contents from __tests__/__fixtures__/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( + `🎭 dry run! This will update 'simple-doc' with contents from __tests__/${fixturesBaseDir}/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( simpleDoc.doc.data )}`, - `🎭 dry run! This will update 'another-doc' with contents from __tests__/__fixtures__/existing-docs/subdir/another-doc.md with the following metadata: ${JSON.stringify( + `🎭 dry run! This will update 'another-doc' with contents from __tests__/${fixturesBaseDir}/existing-docs/subdir/another-doc.md with the following metadata: ${JSON.stringify( anotherDoc.doc.data )}`, ].join('\n') @@ -196,7 +198,7 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(200, { version }); - return docs.run({ folder: './__tests__/__fixtures__/existing-docs', key, version }).then(skippedDocs => { + return docs.run({ folder: `./__tests__/${fixturesBaseDir}/existing-docs`, key, version }).then(skippedDocs => { expect(skippedDocs).toBe( [ '`simple-doc` was not updated because there were no changes.', @@ -226,7 +228,7 @@ describe('rdme docs', () => { .reply(200, { version }); return docs - .run({ dryRun: true, folder: './__tests__/__fixtures__/existing-docs', key, version }) + .run({ dryRun: true, folder: `./__tests__/${fixturesBaseDir}/existing-docs`, key, version }) .then(skippedDocs => { expect(skippedDocs).toBe( [ @@ -244,8 +246,8 @@ describe('rdme docs', () => { describe('new docs', () => { it('should create new doc', async () => { const slug = 'new-doc'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); const getMock = getNockWithVersionHeader(version) .get(`/api/v1/docs/${slug}`) @@ -267,8 +269,8 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(200, { version }); - await expect(docs.run({ folder: './__tests__/__fixtures__/new-docs', key, version })).resolves.toBe( - "🌱 successfully created 'new-doc' with contents from __tests__/__fixtures__/new-docs/new-doc.md" + await expect(docs.run({ folder: `./__tests__/${fixturesBaseDir}/new-docs`, key, version })).resolves.toBe( + `🌱 successfully created 'new-doc' with contents from __tests__/${fixturesBaseDir}/new-docs/new-doc.md` ); getMock.done(); @@ -278,7 +280,7 @@ describe('rdme docs', () => { it('should return creation info for dry run', async () => { const slug = 'new-doc'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); const getMock = getNockWithVersionHeader(version) .get(`/api/v1/docs/${slug}`) @@ -295,8 +297,10 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(200, { version }); - await expect(docs.run({ dryRun: true, folder: './__tests__/__fixtures__/new-docs', key, version })).resolves.toBe( - `🎭 dry run! This will create 'new-doc' with contents from __tests__/__fixtures__/new-docs/new-doc.md with the following metadata: ${JSON.stringify( + await expect( + docs.run({ dryRun: true, folder: `./__tests__/${fixturesBaseDir}/new-docs`, key, version }) + ).resolves.toBe( + `🎭 dry run! This will create 'new-doc' with contents from __tests__/${fixturesBaseDir}/new-docs/new-doc.md with the following metadata: ${JSON.stringify( doc.data )}` ); @@ -315,11 +319,11 @@ describe('rdme docs', () => { message: "We couldn't save this doc (Path `category` is required.).", }; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); - const docTwo = frontMatter(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slugTwo}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); + const docTwo = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); - const hashTwo = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slugTwo}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); + const hashTwo = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); const getMocks = getNockWithVersionHeader(version) .get(`/api/v1/docs/${slug}`) @@ -367,7 +371,7 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(200, { version }); - const fullDirectory = `__tests__/__fixtures__/${folder}`; + const fullDirectory = `__tests__/${fixturesBaseDir}/${folder}`; const formattedErrorObject = { ...errorObject, @@ -387,8 +391,8 @@ describe('rdme docs', () => { describe('slug metadata', () => { it('should use provided slug', async () => { const slug = 'new-doc-slug'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/slug-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/slug-docs/${slug}.md`))); const getMock = getApiNock() .get(`/api/v1/docs/${doc.data.slug}`) @@ -410,8 +414,8 @@ describe('rdme docs', () => { .basicAuth({ user: key }) .reply(200, { version }); - await expect(docs.run({ folder: './__tests__/__fixtures__/slug-docs', key, version })).resolves.toBe( - "🌱 successfully created 'marc-actually-wrote-a-test' with contents from __tests__/__fixtures__/slug-docs/new-doc-slug.md" + await expect(docs.run({ folder: `./__tests__/${fixturesBaseDir}/slug-docs`, key, version })).resolves.toBe( + `🌱 successfully created 'marc-actually-wrote-a-test' with contents from __tests__/${fixturesBaseDir}/slug-docs/new-doc-slug.md` ); getMock.done(); @@ -569,8 +573,8 @@ describe('rdme docs:single', () => { describe('new docs', () => { it('should create new doc', async () => { const slug = 'new-doc'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); const getMock = getNockWithVersionHeader(version) .get(`/api/v1/docs/${slug}`) @@ -593,9 +597,9 @@ describe('rdme docs:single', () => { .reply(200, { version }); await expect( - docsSingle.run({ filePath: './__tests__/__fixtures__/new-docs/new-doc.md', key, version }) + docsSingle.run({ filePath: `./__tests__/${fixturesBaseDir}/new-docs/new-doc.md`, key, version }) ).resolves.toBe( - "🌱 successfully created 'new-doc' with contents from ./__tests__/__fixtures__/new-docs/new-doc.md" + `🌱 successfully created 'new-doc' with contents from ./__tests__/${fixturesBaseDir}/new-docs/new-doc.md` ); getMock.done(); @@ -605,7 +609,7 @@ describe('rdme docs:single', () => { it('should return creation info for dry run', async () => { const slug = 'new-doc'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); const getMock = getNockWithVersionHeader(version) .get(`/api/v1/docs/${slug}`) @@ -623,9 +627,9 @@ describe('rdme docs:single', () => { .reply(200, { version }); await expect( - docsSingle.run({ dryRun: true, filePath: './__tests__/__fixtures__/new-docs/new-doc.md', key, version }) + docsSingle.run({ dryRun: true, filePath: `./__tests__/${fixturesBaseDir}/new-docs/new-doc.md`, key, version }) ).resolves.toBe( - `🎭 dry run! This will create 'new-doc' with contents from ./__tests__/__fixtures__/new-docs/new-doc.md with the following metadata: ${JSON.stringify( + `🎭 dry run! This will create 'new-doc' with contents from ./__tests__/${fixturesBaseDir}/new-docs/new-doc.md with the following metadata: ${JSON.stringify( doc.data )}` ); @@ -643,9 +647,9 @@ describe('rdme docs:single', () => { message: "We couldn't save this doc (Path `category` is required.).", }; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); const getMock = getNockWithVersionHeader(version) .get(`/api/v1/docs/${slug}`) @@ -667,14 +671,14 @@ describe('rdme docs:single', () => { .basicAuth({ user: key }) .reply(200, { version }); - const filePath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; const formattedErrorObject = { ...errorObject, message: `Error uploading ${chalk.underline(`${filePath}`)}:\n\n${errorObject.message}`, }; - await expect(docsSingle.run({ filePath: `${filePath}`, key, version })).rejects.toStrictEqual( + await expect(docsSingle.run({ filePath, key, version })).rejects.toStrictEqual( new APIError(formattedErrorObject) ); @@ -703,14 +707,14 @@ describe('rdme docs:single', () => { .basicAuth({ user: key }) .reply(200, { version }); - const filePath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; const formattedErrorObject = { ...errorObject, message: `Error uploading ${chalk.underline(`${filePath}`)}:\n\n${errorObject.message}`, }; - await expect(docsSingle.run({ filePath: `${filePath}`, key, version })).rejects.toStrictEqual( + await expect(docsSingle.run({ filePath, key, version })).rejects.toStrictEqual( new APIError(formattedErrorObject) ); @@ -722,8 +726,8 @@ describe('rdme docs:single', () => { describe('slug metadata', () => { it('should use provided slug', async () => { const slug = 'new-doc-slug'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/slug-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/slug-docs/${slug}.md`))); const getMock = getApiNock() .get(`/api/v1/docs/${doc.data.slug}`) @@ -746,9 +750,9 @@ describe('rdme docs:single', () => { .reply(200, { version }); await expect( - docsSingle.run({ filePath: './__tests__/__fixtures__/slug-docs/new-doc-slug.md', key, version }) + docsSingle.run({ filePath: `./__tests__/${fixturesBaseDir}/slug-docs/new-doc-slug.md`, key, version }) ).resolves.toBe( - "🌱 successfully created 'marc-actually-wrote-a-test' with contents from ./__tests__/__fixtures__/slug-docs/new-doc-slug.md" + `🌱 successfully created 'marc-actually-wrote-a-test' with contents from ./__tests__/${fixturesBaseDir}/slug-docs/new-doc-slug.md` ); getMock.done(); @@ -761,7 +765,7 @@ describe('rdme docs:single', () => { let simpleDoc; beforeEach(() => { - const fileContents = fs.readFileSync(path.join(fixturesDir, '/existing-docs/simple-doc.md')); + const fileContents = fs.readFileSync(path.join(fullFixturesDir, '/existing-docs/simple-doc.md')); simpleDoc = { slug: 'simple-doc', doc: frontMatter(fileContents), @@ -796,10 +800,10 @@ describe('rdme docs:single', () => { .reply(200, { version }); return docsSingle - .run({ filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) + .run({ filePath: `./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, key, version }) .then(updatedDocs => { expect(updatedDocs).toBe( - "✏️ successfully updated 'simple-doc' with contents from ./__tests__/__fixtures__/existing-docs/simple-doc.md" + `✏️ successfully updated 'simple-doc' with contents from ./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md` ); getMock.done(); @@ -822,13 +826,13 @@ describe('rdme docs:single', () => { .reply(200, { version }); return docsSingle - .run({ dryRun: true, filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) + .run({ dryRun: true, filePath: `./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, key, version }) .then(updatedDocs => { // All docs should have been updated because their hashes from the GET request were different from what they // are currently. expect(updatedDocs).toBe( [ - `🎭 dry run! This will update 'simple-doc' with contents from ./__tests__/__fixtures__/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( + `🎭 dry run! This will update 'simple-doc' with contents from ./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( simpleDoc.doc.data )}`, ].join('\n') @@ -853,7 +857,7 @@ describe('rdme docs:single', () => { .reply(200, { version }); return docsSingle - .run({ filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) + .run({ filePath: `./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, key, version }) .then(skippedDocs => { expect(skippedDocs).toBe('`simple-doc` was not updated because there were no changes.'); @@ -874,7 +878,7 @@ describe('rdme docs:single', () => { .reply(200, { version }); return docsSingle - .run({ dryRun: true, filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key, version }) + .run({ dryRun: true, filePath: `./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, key, version }) .then(skippedDocs => { expect(skippedDocs).toBe('🎭 dry run! `simple-doc` will not be updated because there were no changes.'); From 1b03876f3fc8ea1942a801e98bd4888c5d8fec0a Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 25 Jul 2022 08:05:16 -0500 Subject: [PATCH 4/8] chore: add changelog command category, rearrange --- src/lib/commands.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/lib/commands.js b/src/lib/commands.js index d83f89cce..6548078f5 100644 --- a/src/lib/commands.js +++ b/src/lib/commands.js @@ -68,10 +68,6 @@ exports.list = () => { exports.getCategories = () => { return { - admin: { - description: 'Administration', - commands: [], - }, apis: { description: 'Upload OpenAPI/Swagger definitions', commands: [], @@ -80,14 +76,22 @@ exports.getCategories = () => { description: 'Documentation', commands: [], }, - versions: { - description: 'Versions', + changelogs: { + description: 'Changelog', commands: [], }, categories: { description: 'Categories', commands: [], }, + versions: { + description: 'Versions', + commands: [], + }, + admin: { + description: 'Administration', + commands: [], + }, utilities: { description: 'Other useful commands', commands: [], From 68a15f6bb36fb389c634e2509e5044c9af5518e2 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 25 Jul 2022 16:18:02 -0500 Subject: [PATCH 5/8] feat: initial pass at changelog commands --- src/cmds/changelogs/index.js | 81 +++++++++++++++++++++++++++++++++++ src/cmds/changelogs/single.js | 56 ++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/cmds/changelogs/index.js create mode 100644 src/cmds/changelogs/single.js diff --git a/src/cmds/changelogs/index.js b/src/cmds/changelogs/index.js new file mode 100644 index 000000000..a69508285 --- /dev/null +++ b/src/cmds/changelogs/index.js @@ -0,0 +1,81 @@ +const chalk = require('chalk'); +const config = require('config'); +const fs = require('fs'); +const path = require('path'); + +const { debug } = require('../../lib/logger'); +const pushDoc = require('../../lib/pushDoc'); + +module.exports = class ChangelogsCommand { + constructor() { + this.command = 'changelogs'; + this.usage = 'changelogs [options]'; + this.description = 'Sync a folder of Markdown files to your ReadMe project as Changelog posts.'; + this.category = 'changelogs'; + this.position = 1; + + this.hiddenArgs = ['folder']; + this.args = [ + { + name: 'key', + type: String, + description: 'Project API key', + }, + { + name: 'folder', + type: String, + defaultOption: true, + }, + { + name: 'dryRun', + type: Boolean, + description: 'Runs the command without creating/updating any changelogs in ReadMe. Useful for debugging.', + }, + ]; + } + + async run(opts) { + const { dryRun, folder, key } = opts; + + debug(`command: ${this.command}`); + debug(`opts: ${JSON.stringify(opts)}`); + + if (!key) { + return Promise.reject(new Error('No project API key provided. Please use `--key`.')); + } + + if (!folder) { + return Promise.reject(new Error(`No folder provided. Usage \`${config.get('cli')} ${this.usage}\`.`)); + } + + // Find the files to sync + const readdirRecursive = folderToSearch => { + const filesInFolder = fs.readdirSync(folderToSearch, { withFileTypes: true }); + const files = filesInFolder + .filter(fileHandle => fileHandle.isFile()) + .map(fileHandle => path.join(folderToSearch, fileHandle.name)); + const folders = filesInFolder.filter(fileHandle => fileHandle.isDirectory()); + const subFiles = [].concat( + ...folders.map(fileHandle => readdirRecursive(path.join(folderToSearch, fileHandle.name))) + ); + return [...files, ...subFiles]; + }; + + // Strip out non-markdown files + const files = readdirRecursive(folder).filter(file => file.endsWith('.md') || file.endsWith('.markdown')); + + debug(`number of files: ${files.length}`); + + if (!files.length) { + return Promise.reject(new Error(`We were unable to locate Markdown files in ${folder}.`)); + } + + const updatedDocs = await Promise.all( + files.map(async filename => { + return pushDoc(key, undefined, dryRun, filename, this.category); + }) + ); + + return chalk.green(updatedDocs.join('\n')); + } +}; diff --git a/src/cmds/changelogs/single.js b/src/cmds/changelogs/single.js new file mode 100644 index 000000000..d20d0a4fa --- /dev/null +++ b/src/cmds/changelogs/single.js @@ -0,0 +1,56 @@ +const chalk = require('chalk'); +const config = require('config'); + +const { debug } = require('../../lib/logger'); +const pushDoc = require('../../lib/pushDoc'); + +module.exports = class SingleChangelogCommand { + constructor() { + this.command = 'changelogs:single'; + this.usage = 'changelogs:single [options]'; + this.description = 'Sync a single Markdown file to your ReadMe project as a Changelog post.'; + this.category = 'changelogs'; + this.position = 3; + + this.hiddenArgs = ['filePath']; + this.args = [ + { + name: 'key', + type: String, + description: 'Project API key', + }, + { + name: 'filePath', + type: String, + defaultOption: true, + }, + { + name: 'dryRun', + type: Boolean, + description: 'Runs the command without creating/updating any changelogs in ReadMe. Useful for debugging.', + }, + ]; + } + + async run(opts) { + const { dryRun, filePath, key } = opts; + debug(`command: ${this.command}`); + debug(`opts: ${JSON.stringify(opts)}`); + + if (!key) { + return Promise.reject(new Error('No project API key provided. Please use `--key`.')); + } + + if (!filePath) { + return Promise.reject(new Error(`No file path provided. Usage \`${config.get('cli')} ${this.usage}\`.`)); + } + + if (filePath.endsWith('.md') === false || !filePath.endsWith('.markdown') === false) { + return Promise.reject(new Error('The file path specified is not a markdown file.')); + } + + const createdDoc = await pushDoc(key, undefined, dryRun, filePath, this.category); + + return chalk.green(createdDoc); + } +}; From 11f2b564c69d042256eedab2361cf41fc365b1ea Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 25 Jul 2022 16:54:09 -0500 Subject: [PATCH 6/8] test: add test suite let's hope this is right lol --- __tests__/cmds/changelogs.test.js | 628 ++++++++++++++++++++++++++++++ 1 file changed, 628 insertions(+) create mode 100644 __tests__/cmds/changelogs.test.js diff --git a/__tests__/cmds/changelogs.test.js b/__tests__/cmds/changelogs.test.js new file mode 100644 index 000000000..a231e460c --- /dev/null +++ b/__tests__/cmds/changelogs.test.js @@ -0,0 +1,628 @@ +const nock = require('nock'); +const chalk = require('chalk'); +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); +const frontMatter = require('gray-matter'); + +const APIError = require('../../src/lib/apiError'); +const getApiNock = require('../get-api-nock'); + +const ChangelogsCommand = require('../../src/cmds/changelogs'); +const SingleChangelogCommand = require('../../src/cmds/changelogs/single'); + +const changelogs = new ChangelogsCommand(); +const changelogsSingle = new SingleChangelogCommand(); + +const fixturesDir = `${__dirname}./../__fixtures__`; +const key = 'API_KEY'; + +function hashFileContents(contents) { + return crypto.createHash('sha1').update(contents).digest('hex'); +} + +describe('rdme changelogs', () => { + beforeAll(() => nock.disableNetConnect()); + + afterAll(() => nock.cleanAll()); + + it('should error if no api key provided', () => { + return expect(changelogs.run({})).rejects.toThrow('No project API key provided. Please use `--key`.'); + }); + + it('should error if no folder provided', () => { + return expect(changelogs.run({ key, version: '1.0.0' })).rejects.toThrow( + 'No folder provided. Usage `rdme changelogs [options]`.' + ); + }); + + it('should error if the argument isnt a folder', () => { + return expect(changelogs.run({ key, version: '1.0.0', folder: 'not-a-folder' })).rejects.toThrow( + "ENOENT: no such file or directory, scandir 'not-a-folder'" + ); + }); + + it('should error if the folder contains no markdown files', () => { + return expect(changelogs.run({ key, version: '1.0.0', folder: '.github/workflows' })).rejects.toThrow( + 'We were unable to locate Markdown files in .github/workflows.' + ); + }); + + describe('existing changelogs', () => { + let simpleDoc; + let anotherDoc; + + beforeEach(() => { + let fileContents = fs.readFileSync(path.join(fixturesDir, '/existing-docs/simple-doc.md')); + simpleDoc = { + slug: 'simple-doc', + doc: frontMatter(fileContents), + hash: hashFileContents(fileContents), + }; + + fileContents = fs.readFileSync(path.join(fixturesDir, '/existing-docs/subdir/another-doc.md')); + anotherDoc = { + slug: 'another-doc', + doc: frontMatter(fileContents), + hash: hashFileContents(fileContents), + }; + }); + + it('should fetch changelog and merge with what is returned', () => { + expect.assertions(1); + + const getMocks = getApiNock() + .get('/api/v1/changelogs/simple-doc') + .basicAuth({ user: key }) + .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: 'anOldHash' }) + .get('/api/v1/changelogs/another-doc') + .basicAuth({ user: key }) + .reply(200, { slug: anotherDoc.slug, lastUpdatedHash: 'anOldHash' }); + + const updateMocks = getApiNock() + .put('/api/v1/changelogs/simple-doc', { + slug: simpleDoc.slug, + body: simpleDoc.doc.content, + lastUpdatedHash: simpleDoc.hash, + ...simpleDoc.doc.data, + }) + .basicAuth({ user: key }) + .reply(200, { + slug: simpleDoc.slug, + body: simpleDoc.doc.content, + }) + .put('/api/v1/changelogs/another-doc', { + slug: anotherDoc.slug, + body: anotherDoc.doc.content, + lastUpdatedHash: anotherDoc.hash, + ...anotherDoc.doc.data, + }) + .basicAuth({ user: key }) + .reply(200, { slug: anotherDoc.slug, body: anotherDoc.doc.content }); + + return changelogs.run({ folder: './__tests__/__fixtures__/existing-docs', key }).then(updatedDocs => { + // All changelogs should have been updated because their hashes from the GET request were different from what they + // are currently. + expect(updatedDocs).toBe( + [ + "✏️ successfully updated 'simple-doc' with contents from __tests__/__fixtures__/existing-docs/simple-doc.md", + "✏️ successfully updated 'another-doc' with contents from __tests__/__fixtures__/existing-docs/subdir/another-doc.md", + ].join('\n') + ); + + getMocks.done(); + updateMocks.done(); + }); + }); + + it('should return changelog update info for dry run', () => { + expect.assertions(1); + + const getMocks = getApiNock() + .get('/api/v1/changelogs/simple-doc') + .basicAuth({ user: key }) + .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: 'anOldHash' }) + .get('/api/v1/changelogs/another-doc') + .basicAuth({ user: key }) + .reply(200, { slug: anotherDoc.slug, lastUpdatedHash: 'anOldHash' }); + + return changelogs + .run({ dryRun: true, folder: './__tests__/__fixtures__/existing-docs', key }) + .then(updatedDocs => { + // All changelogs should have been updated because their hashes from the GET request were different from what they + // are currently. + expect(updatedDocs).toBe( + [ + `🎭 dry run! This will update 'simple-doc' with contents from __tests__/__fixtures__/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( + simpleDoc.doc.data + )}`, + `🎭 dry run! This will update 'another-doc' with contents from __tests__/__fixtures__/existing-docs/subdir/another-doc.md with the following metadata: ${JSON.stringify( + anotherDoc.doc.data + )}`, + ].join('\n') + ); + + getMocks.done(); + }); + }); + + it('should not send requests for changelogs that have not changed', () => { + expect.assertions(1); + + const getMocks = getApiNock() + .get('/api/v1/changelogs/simple-doc') + .basicAuth({ user: key }) + .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: simpleDoc.hash }) + .get('/api/v1/changelogs/another-doc') + .basicAuth({ user: key }) + .reply(200, { slug: anotherDoc.slug, lastUpdatedHash: anotherDoc.hash }); + + return changelogs.run({ folder: './__tests__/__fixtures__/existing-docs', key }).then(skippedDocs => { + expect(skippedDocs).toBe( + [ + '`simple-doc` was not updated because there were no changes.', + '`another-doc` was not updated because there were no changes.', + ].join('\n') + ); + + getMocks.done(); + }); + }); + + it('should adjust "no changes" message if in dry run', () => { + expect.assertions(1); + + const getMocks = getApiNock() + .get('/api/v1/changelogs/simple-doc') + .basicAuth({ user: key }) + .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: simpleDoc.hash }) + .get('/api/v1/changelogs/another-doc') + .basicAuth({ user: key }) + .reply(200, { slug: anotherDoc.slug, lastUpdatedHash: anotherDoc.hash }); + + return changelogs + .run({ dryRun: true, folder: './__tests__/__fixtures__/existing-docs', key }) + .then(skippedDocs => { + expect(skippedDocs).toBe( + [ + '🎭 dry run! `simple-doc` will not be updated because there were no changes.', + '🎭 dry run! `another-doc` will not be updated because there were no changes.', + ].join('\n') + ); + + getMocks.done(); + }); + }); + }); + + describe('new changelogs', () => { + it('should create new changelog', async () => { + const slug = 'new-doc'; + const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + + const getMock = getApiNock() + .get(`/api/v1/changelogs/${slug}`) + .basicAuth({ user: key }) + .reply(404, { + error: 'CHANGELOG_NOTFOUND', + message: `The changelog with the slug '${slug}' couldn't be found`, + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }); + + const postMock = getApiNock() + .post('/api/v1/changelogs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) + .basicAuth({ user: key }) + .reply(201, { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }); + + await expect(changelogs.run({ folder: './__tests__/__fixtures__/new-docs', key })).resolves.toBe( + "🌱 successfully created 'new-doc' with contents from __tests__/__fixtures__/new-docs/new-doc.md" + ); + + getMock.done(); + postMock.done(); + }); + + it('should return creation info for dry run', async () => { + const slug = 'new-doc'; + const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + + const getMock = getApiNock() + .get(`/api/v1/changelogs/${slug}`) + .basicAuth({ user: key }) + .reply(404, { + error: 'CHANGELOG_NOTFOUND', + message: `The changelog with the slug '${slug}' couldn't be found`, + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }); + + await expect(changelogs.run({ dryRun: true, folder: './__tests__/__fixtures__/new-docs', key })).resolves.toBe( + `🎭 dry run! This will create 'new-doc' with contents from __tests__/__fixtures__/new-docs/new-doc.md with the following metadata: ${JSON.stringify( + doc.data + )}` + ); + + getMock.done(); + }); + + it('should fail if any changelogs are invalid', async () => { + const folder = 'failure-docs'; + const slug = 'fail-doc'; + const slugTwo = 'new-doc'; + + const errorObject = { + error: 'CHANGELOG_INVALID', + message: "We couldn't save this changelog (Changelog title cannot be blank).", + suggestion: 'Make sure all the data is correct, and the body is valid Markdown.', + docs: 'fake-metrics-uuid', + help: "If you need help, email support@readme.io and include the following link to your API log: 'fake-metrics-uuid'.", + }; + const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); + const docTwo = frontMatter(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slugTwo}.md`))); + + const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); + const hashTwo = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slugTwo}.md`))); + + const getMocks = getApiNock() + .get(`/api/v1/changelogs/${slug}`) + .basicAuth({ user: key }) + .reply(404, { + error: 'CHANGELOG_NOTFOUND', + message: `The changelog with the slug '${slug}' couldn't be found`, + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }) + .get(`/api/v1/changelogs/${slugTwo}`) + .basicAuth({ user: key }) + .reply(404, { + error: 'CHANGELOG_NOTFOUND', + message: `The changelog with the slug '${slugTwo}' couldn't be found`, + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }); + + const postMocks = getApiNock() + .post('/api/v1/changelogs', { slug: slugTwo, body: docTwo.content, ...docTwo.data, lastUpdatedHash: hashTwo }) + .basicAuth({ user: key }) + .reply(201, { + metadata: { image: [], title: '', description: '' }, + title: 'This is the document title', + slug: slugTwo, + body: 'Body', + }) + .post('/api/v1/changelogs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) + .basicAuth({ user: key }) + .reply(400, errorObject); + + const fullDirectory = `__tests__/__fixtures__/${folder}`; + + const formattedErrorObject = { + ...errorObject, + message: `Error uploading ${chalk.underline(`${fullDirectory}/${slug}.md`)}:\n\n${errorObject.message}`, + }; + + await expect(changelogs.run({ folder: `./${fullDirectory}`, key })).rejects.toStrictEqual( + new APIError(formattedErrorObject) + ); + + getMocks.done(); + postMocks.done(); + }); + }); + + describe('slug metadata', () => { + it('should use provided slug', async () => { + const slug = 'new-doc-slug'; + const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); + + const getMock = getApiNock() + .get(`/api/v1/changelogs/${doc.data.slug}`) + .basicAuth({ user: key }) + .reply(404, { + error: 'CHANGELOG_NOTFOUND', + message: `The changelog with the slug '${slug}' couldn't be found`, + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }); + + const postMock = getApiNock() + .post('/api/v1/changelogs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) + .basicAuth({ user: key }) + .reply(201, { slug: doc.data.slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }); + + await expect(changelogs.run({ folder: './__tests__/__fixtures__/slug-docs', key })).resolves.toBe( + "🌱 successfully created 'marc-actually-wrote-a-test' with contents from __tests__/__fixtures__/slug-docs/new-doc-slug.md" + ); + + getMock.done(); + postMock.done(); + }); + }); +}); + +describe('rdme changelogs:single', () => { + beforeAll(() => nock.disableNetConnect()); + + afterAll(() => nock.cleanAll()); + + it('should error if no api key provided', () => { + return expect(changelogsSingle.run({})).rejects.toThrow('No project API key provided. Please use `--key`.'); + }); + + it('should error if no file path provided', () => { + return expect(changelogsSingle.run({ key, version: '1.0.0' })).rejects.toThrow( + 'No file path provided. Usage `rdme changelogs:single [options]`.' + ); + }); + + it('should error if the argument is not a markdown file', async () => { + await expect(changelogsSingle.run({ key, version: '1.0.0', filePath: 'not-a-markdown-file' })).rejects.toThrow( + 'The file path specified is not a markdown file.' + ); + }); + + describe('new changelogs', () => { + it('should create new changelog', async () => { + const slug = 'new-doc'; + const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + + const getMock = getApiNock() + .get(`/api/v1/changelogs/${slug}`) + .basicAuth({ user: key }) + .reply(404, { + error: 'CHANGELOG_NOTFOUND', + message: `The changelog with the slug '${slug}' couldn't be found`, + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }); + + const postMock = getApiNock() + .post('/api/v1/changelogs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) + .basicAuth({ user: key }) + .reply(201, { slug, body: doc.content, ...doc.data }); + + await expect( + changelogsSingle.run({ filePath: './__tests__/__fixtures__/new-docs/new-doc.md', key }) + ).resolves.toBe( + "🌱 successfully created 'new-doc' with contents from ./__tests__/__fixtures__/new-docs/new-doc.md" + ); + + getMock.done(); + postMock.done(); + }); + + it('should return creation info for dry run', async () => { + const slug = 'new-doc'; + const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + + const getMock = getApiNock() + .get(`/api/v1/changelogs/${slug}`) + .basicAuth({ user: key }) + .reply(404, { + error: 'CHANGELOG_NOTFOUND', + message: `The changelog with the slug '${slug}' couldn't be found`, + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }); + + await expect( + changelogsSingle.run({ dryRun: true, filePath: './__tests__/__fixtures__/new-docs/new-doc.md', key }) + ).resolves.toBe( + `🎭 dry run! This will create 'new-doc' with contents from ./__tests__/__fixtures__/new-docs/new-doc.md with the following metadata: ${JSON.stringify( + doc.data + )}` + ); + + getMock.done(); + }); + + it('should fail if the changelog is invalid', async () => { + const folder = 'failure-docs'; + const slug = 'fail-doc'; + + const errorObject = { + error: 'CHANGELOG_INVALID', + message: "We couldn't save this changelog (Changelog title cannot be blank).", + suggestion: 'Make sure all the data is correct, and the body is valid Markdown.', + docs: 'fake-metrics-uuid', + help: "If you need help, email support@readme.io and include the following link to your API log: 'fake-metrics-uuid'.", + }; + + const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); + + const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); + + const getMock = getApiNock() + .get(`/api/v1/changelogs/${slug}`) + .basicAuth({ user: key }) + .reply(404, { + error: 'CHANGELOG_NOTFOUND', + message: `The changelog with the slug '${slug}' couldn't be found`, + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }); + + const postMock = getApiNock() + .post('/api/v1/changelogs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) + .basicAuth({ user: key }) + .reply(400, errorObject); + + const filePath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; + + const formattedErrorObject = { + ...errorObject, + message: `Error uploading ${chalk.underline(`${filePath}`)}:\n\n${errorObject.message}`, + }; + + await expect(changelogsSingle.run({ filePath: `${filePath}`, key })).rejects.toStrictEqual( + new APIError(formattedErrorObject) + ); + + getMock.done(); + postMock.done(); + }); + + it('should fail if some other error when retrieving page slug', async () => { + const slug = 'fail-doc'; + + const errorObject = { + error: 'INTERNAL_ERROR', + message: 'Unknown error (yikes)', + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }; + + const getMock = getApiNock().get(`/api/v1/changelogs/${slug}`).basicAuth({ user: key }).reply(500, errorObject); + + const filePath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; + + const formattedErrorObject = { + ...errorObject, + message: `Error uploading ${chalk.underline(`${filePath}`)}:\n\n${errorObject.message}`, + }; + + await expect(changelogsSingle.run({ filePath: `${filePath}`, key })).rejects.toStrictEqual( + new APIError(formattedErrorObject) + ); + + getMock.done(); + }); + }); + + describe('slug metadata', () => { + it('should use provided slug', async () => { + const slug = 'new-doc-slug'; + const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); + + const getMock = getApiNock() + .get(`/api/v1/changelogs/${doc.data.slug}`) + .basicAuth({ user: key }) + .reply(404, { + error: 'CHANGELOG_NOTFOUND', + message: `The changelog with the slug '${slug}' couldn't be found`, + suggestion: '...a suggestion to resolve the issue...', + help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', + }); + + const postMock = getApiNock() + .post('/api/v1/changelogs', { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }) + .basicAuth({ user: key }) + .reply(201, { slug: doc.data.slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }); + + await expect( + changelogsSingle.run({ filePath: './__tests__/__fixtures__/slug-docs/new-doc-slug.md', key }) + ).resolves.toBe( + "🌱 successfully created 'marc-actually-wrote-a-test' with contents from ./__tests__/__fixtures__/slug-docs/new-doc-slug.md" + ); + + getMock.done(); + postMock.done(); + }); + }); + + describe('existing changelogs', () => { + let simpleDoc; + + beforeEach(() => { + const fileContents = fs.readFileSync(path.join(fixturesDir, '/existing-docs/simple-doc.md')); + simpleDoc = { + slug: 'simple-doc', + doc: frontMatter(fileContents), + hash: hashFileContents(fileContents), + }; + }); + + it('should fetch changelog and merge with what is returned', () => { + const getMock = getApiNock() + .get('/api/v1/changelogs/simple-doc') + .basicAuth({ user: key }) + .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: 'anOldHash' }); + + const updateMock = getApiNock() + .put('/api/v1/changelogs/simple-doc', { + slug: simpleDoc.slug, + body: simpleDoc.doc.content, + lastUpdatedHash: simpleDoc.hash, + ...simpleDoc.doc.data, + }) + .basicAuth({ user: key }) + .reply(200, { + slug: simpleDoc.slug, + body: simpleDoc.doc.content, + }); + + return changelogsSingle + .run({ filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key }) + .then(updatedDocs => { + expect(updatedDocs).toBe( + "✏️ successfully updated 'simple-doc' with contents from ./__tests__/__fixtures__/existing-docs/simple-doc.md" + ); + + getMock.done(); + updateMock.done(); + }); + }); + + it('should return changelog update info for dry run', () => { + expect.assertions(1); + + const getMock = getApiNock() + .get('/api/v1/changelogs/simple-doc') + .basicAuth({ user: key }) + .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: 'anOldHash' }); + + return changelogsSingle + .run({ dryRun: true, filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key }) + .then(updatedDocs => { + // All changelogs should have been updated because their hashes from the GET request were different from what they + // are currently. + expect(updatedDocs).toBe( + [ + `🎭 dry run! This will update 'simple-doc' with contents from ./__tests__/__fixtures__/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( + simpleDoc.doc.data + )}`, + ].join('\n') + ); + + getMock.done(); + }); + }); + + it('should not send requests for changelogs that have not changed', () => { + expect.assertions(1); + + const getMock = getApiNock() + .get('/api/v1/changelogs/simple-doc') + .basicAuth({ user: key }) + .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: simpleDoc.hash }); + + return changelogsSingle + .run({ filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key }) + .then(skippedDocs => { + expect(skippedDocs).toBe('`simple-doc` was not updated because there were no changes.'); + + getMock.done(); + }); + }); + + it('should adjust "no changes" message if in dry run', () => { + const getMock = getApiNock() + .get('/api/v1/changelogs/simple-doc') + .basicAuth({ user: key }) + .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: simpleDoc.hash }); + + return changelogsSingle + .run({ dryRun: true, filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key }) + .then(skippedDocs => { + expect(skippedDocs).toBe('🎭 dry run! `simple-doc` will not be updated because there were no changes.'); + + getMock.done(); + }); + }); + }); +}); From 8955c756a98161fb2b43c38125273b9274099fe2 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 25 Jul 2022 16:54:22 -0500 Subject: [PATCH 7/8] docs: update README.md --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index efadb933b..be5dff9fa 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,26 @@ rdme docs:edit --version={project-version} rdme docs:single path-to-markdown-file --version={project-version} ``` +### Changelogs + +#### Syncing a Folder of Markdown to ReadMe + +The Markdown files will require YAML front matter with certain ReadMe documentation attributes. Check out [our docs](https://docs.readme.com/docs/rdme#markdown-file-setup) for more info on setting up your front matter. + +Passing in a path to a directory will also sync any Markdown files that are located in subdirectories. + +```sh +rdme changelogs path-to-markdown-files +``` + +This command also has a dry run mode, which can be useful for initial setup and debugging. You can read more about dry run mode [in our docs](https://docs.readme.com/docs/rdme#dry-run-mode). + +#### Syncing a Single Markdown File to ReadMe + +```sh +rdme changelogs:single path-to-markdown-file +``` + ### Versions #### Get All Versions Associated With Your Project From aaad56d984da47ed3576a8b672de5b848a07ff8d Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 25 Jul 2022 18:45:32 -0500 Subject: [PATCH 8/8] test: use fixtures in changelog subdir --- .../existing-docs/not-a-markdown-file | 0 .../changelogs/existing-docs/simple-doc.md | 4 + .../existing-docs/subdir/another-doc.md | 4 + .../changelogs/failure-docs/fail-doc.md | 1 + .../changelogs/failure-docs/new-doc.md | 6 + .../changelogs/new-docs/new-doc.md | 6 + .../changelogs/slug-docs/new-doc-slug.md | 7 ++ __tests__/cmds/changelogs.test.js | 103 +++++++++--------- 8 files changed, 81 insertions(+), 50 deletions(-) create mode 100644 __tests__/__fixtures__/changelogs/existing-docs/not-a-markdown-file create mode 100644 __tests__/__fixtures__/changelogs/existing-docs/simple-doc.md create mode 100644 __tests__/__fixtures__/changelogs/existing-docs/subdir/another-doc.md create mode 100644 __tests__/__fixtures__/changelogs/failure-docs/fail-doc.md create mode 100644 __tests__/__fixtures__/changelogs/failure-docs/new-doc.md create mode 100644 __tests__/__fixtures__/changelogs/new-docs/new-doc.md create mode 100644 __tests__/__fixtures__/changelogs/slug-docs/new-doc-slug.md diff --git a/__tests__/__fixtures__/changelogs/existing-docs/not-a-markdown-file b/__tests__/__fixtures__/changelogs/existing-docs/not-a-markdown-file new file mode 100644 index 000000000..e69de29bb diff --git a/__tests__/__fixtures__/changelogs/existing-docs/simple-doc.md b/__tests__/__fixtures__/changelogs/existing-docs/simple-doc.md new file mode 100644 index 000000000..c55d504a0 --- /dev/null +++ b/__tests__/__fixtures__/changelogs/existing-docs/simple-doc.md @@ -0,0 +1,4 @@ +--- +title: This is the changelog title +--- +Body diff --git a/__tests__/__fixtures__/changelogs/existing-docs/subdir/another-doc.md b/__tests__/__fixtures__/changelogs/existing-docs/subdir/another-doc.md new file mode 100644 index 000000000..2d95d7732 --- /dev/null +++ b/__tests__/__fixtures__/changelogs/existing-docs/subdir/another-doc.md @@ -0,0 +1,4 @@ +--- +title: This is another changelog title +--- +Another body diff --git a/__tests__/__fixtures__/changelogs/failure-docs/fail-doc.md b/__tests__/__fixtures__/changelogs/failure-docs/fail-doc.md new file mode 100644 index 000000000..e8eba3dc1 --- /dev/null +++ b/__tests__/__fixtures__/changelogs/failure-docs/fail-doc.md @@ -0,0 +1 @@ +Body diff --git a/__tests__/__fixtures__/changelogs/failure-docs/new-doc.md b/__tests__/__fixtures__/changelogs/failure-docs/new-doc.md new file mode 100644 index 000000000..1a8070bac --- /dev/null +++ b/__tests__/__fixtures__/changelogs/failure-docs/new-doc.md @@ -0,0 +1,6 @@ +--- +type: added +title: This is the changelog title +--- + +Body diff --git a/__tests__/__fixtures__/changelogs/new-docs/new-doc.md b/__tests__/__fixtures__/changelogs/new-docs/new-doc.md new file mode 100644 index 000000000..1a8070bac --- /dev/null +++ b/__tests__/__fixtures__/changelogs/new-docs/new-doc.md @@ -0,0 +1,6 @@ +--- +type: added +title: This is the changelog title +--- + +Body diff --git a/__tests__/__fixtures__/changelogs/slug-docs/new-doc-slug.md b/__tests__/__fixtures__/changelogs/slug-docs/new-doc-slug.md new file mode 100644 index 000000000..a53bd8ca2 --- /dev/null +++ b/__tests__/__fixtures__/changelogs/slug-docs/new-doc-slug.md @@ -0,0 +1,7 @@ +--- +type: added +title: This is the changelog title +slug: marc-actually-wrote-a-test +--- + +Body diff --git a/__tests__/cmds/changelogs.test.js b/__tests__/cmds/changelogs.test.js index a231e460c..f8ecc0cc1 100644 --- a/__tests__/cmds/changelogs.test.js +++ b/__tests__/cmds/changelogs.test.js @@ -14,7 +14,8 @@ const SingleChangelogCommand = require('../../src/cmds/changelogs/single'); const changelogs = new ChangelogsCommand(); const changelogsSingle = new SingleChangelogCommand(); -const fixturesDir = `${__dirname}./../__fixtures__`; +const fixturesBaseDir = '__fixtures__/changelogs'; +const fullFixturesDir = `${__dirname}./../${fixturesBaseDir}`; const key = 'API_KEY'; function hashFileContents(contents) { @@ -53,14 +54,14 @@ describe('rdme changelogs', () => { let anotherDoc; beforeEach(() => { - let fileContents = fs.readFileSync(path.join(fixturesDir, '/existing-docs/simple-doc.md')); + let fileContents = fs.readFileSync(path.join(fullFixturesDir, '/existing-docs/simple-doc.md')); simpleDoc = { slug: 'simple-doc', doc: frontMatter(fileContents), hash: hashFileContents(fileContents), }; - fileContents = fs.readFileSync(path.join(fixturesDir, '/existing-docs/subdir/another-doc.md')); + fileContents = fs.readFileSync(path.join(fullFixturesDir, '/existing-docs/subdir/another-doc.md')); anotherDoc = { slug: 'another-doc', doc: frontMatter(fileContents), @@ -100,13 +101,13 @@ describe('rdme changelogs', () => { .basicAuth({ user: key }) .reply(200, { slug: anotherDoc.slug, body: anotherDoc.doc.content }); - return changelogs.run({ folder: './__tests__/__fixtures__/existing-docs', key }).then(updatedDocs => { + return changelogs.run({ folder: `./__tests__/${fixturesBaseDir}/existing-docs`, key }).then(updatedDocs => { // All changelogs should have been updated because their hashes from the GET request were different from what they // are currently. expect(updatedDocs).toBe( [ - "✏️ successfully updated 'simple-doc' with contents from __tests__/__fixtures__/existing-docs/simple-doc.md", - "✏️ successfully updated 'another-doc' with contents from __tests__/__fixtures__/existing-docs/subdir/another-doc.md", + `✏️ successfully updated 'simple-doc' with contents from __tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, + `✏️ successfully updated 'another-doc' with contents from __tests__/${fixturesBaseDir}/existing-docs/subdir/another-doc.md`, ].join('\n') ); @@ -127,16 +128,16 @@ describe('rdme changelogs', () => { .reply(200, { slug: anotherDoc.slug, lastUpdatedHash: 'anOldHash' }); return changelogs - .run({ dryRun: true, folder: './__tests__/__fixtures__/existing-docs', key }) + .run({ dryRun: true, folder: `./__tests__/${fixturesBaseDir}/existing-docs`, key }) .then(updatedDocs => { // All changelogs should have been updated because their hashes from the GET request were different from what they // are currently. expect(updatedDocs).toBe( [ - `🎭 dry run! This will update 'simple-doc' with contents from __tests__/__fixtures__/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( + `🎭 dry run! This will update 'simple-doc' with contents from __tests__/${fixturesBaseDir}/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( simpleDoc.doc.data )}`, - `🎭 dry run! This will update 'another-doc' with contents from __tests__/__fixtures__/existing-docs/subdir/another-doc.md with the following metadata: ${JSON.stringify( + `🎭 dry run! This will update 'another-doc' with contents from __tests__/${fixturesBaseDir}/existing-docs/subdir/another-doc.md with the following metadata: ${JSON.stringify( anotherDoc.doc.data )}`, ].join('\n') @@ -157,7 +158,7 @@ describe('rdme changelogs', () => { .basicAuth({ user: key }) .reply(200, { slug: anotherDoc.slug, lastUpdatedHash: anotherDoc.hash }); - return changelogs.run({ folder: './__tests__/__fixtures__/existing-docs', key }).then(skippedDocs => { + return changelogs.run({ folder: `./__tests__/${fixturesBaseDir}/existing-docs`, key }).then(skippedDocs => { expect(skippedDocs).toBe( [ '`simple-doc` was not updated because there were no changes.', @@ -181,7 +182,7 @@ describe('rdme changelogs', () => { .reply(200, { slug: anotherDoc.slug, lastUpdatedHash: anotherDoc.hash }); return changelogs - .run({ dryRun: true, folder: './__tests__/__fixtures__/existing-docs', key }) + .run({ dryRun: true, folder: `./__tests__/${fixturesBaseDir}/existing-docs`, key }) .then(skippedDocs => { expect(skippedDocs).toBe( [ @@ -198,8 +199,8 @@ describe('rdme changelogs', () => { describe('new changelogs', () => { it('should create new changelog', async () => { const slug = 'new-doc'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); const getMock = getApiNock() .get(`/api/v1/changelogs/${slug}`) @@ -216,8 +217,8 @@ describe('rdme changelogs', () => { .basicAuth({ user: key }) .reply(201, { slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }); - await expect(changelogs.run({ folder: './__tests__/__fixtures__/new-docs', key })).resolves.toBe( - "🌱 successfully created 'new-doc' with contents from __tests__/__fixtures__/new-docs/new-doc.md" + await expect(changelogs.run({ folder: `./__tests__/${fixturesBaseDir}/new-docs`, key })).resolves.toBe( + `🌱 successfully created 'new-doc' with contents from __tests__/${fixturesBaseDir}/new-docs/new-doc.md` ); getMock.done(); @@ -226,7 +227,7 @@ describe('rdme changelogs', () => { it('should return creation info for dry run', async () => { const slug = 'new-doc'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); const getMock = getApiNock() .get(`/api/v1/changelogs/${slug}`) @@ -238,8 +239,10 @@ describe('rdme changelogs', () => { help: 'If you need help, email support@readme.io and mention log "fake-metrics-uuid".', }); - await expect(changelogs.run({ dryRun: true, folder: './__tests__/__fixtures__/new-docs', key })).resolves.toBe( - `🎭 dry run! This will create 'new-doc' with contents from __tests__/__fixtures__/new-docs/new-doc.md with the following metadata: ${JSON.stringify( + await expect( + changelogs.run({ dryRun: true, folder: `./__tests__/${fixturesBaseDir}/new-docs`, key }) + ).resolves.toBe( + `🎭 dry run! This will create 'new-doc' with contents from __tests__/${fixturesBaseDir}/new-docs/new-doc.md with the following metadata: ${JSON.stringify( doc.data )}` ); @@ -259,11 +262,11 @@ describe('rdme changelogs', () => { docs: 'fake-metrics-uuid', help: "If you need help, email support@readme.io and include the following link to your API log: 'fake-metrics-uuid'.", }; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); - const docTwo = frontMatter(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slugTwo}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); + const docTwo = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); - const hashTwo = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slugTwo}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); + const hashTwo = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slugTwo}.md`))); const getMocks = getApiNock() .get(`/api/v1/changelogs/${slug}`) @@ -288,7 +291,7 @@ describe('rdme changelogs', () => { .basicAuth({ user: key }) .reply(201, { metadata: { image: [], title: '', description: '' }, - title: 'This is the document title', + title: 'This is the changelog title', slug: slugTwo, body: 'Body', }) @@ -296,7 +299,7 @@ describe('rdme changelogs', () => { .basicAuth({ user: key }) .reply(400, errorObject); - const fullDirectory = `__tests__/__fixtures__/${folder}`; + const fullDirectory = `__tests__/${fixturesBaseDir}/${folder}`; const formattedErrorObject = { ...errorObject, @@ -315,8 +318,8 @@ describe('rdme changelogs', () => { describe('slug metadata', () => { it('should use provided slug', async () => { const slug = 'new-doc-slug'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/slug-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/slug-docs/${slug}.md`))); const getMock = getApiNock() .get(`/api/v1/changelogs/${doc.data.slug}`) @@ -333,8 +336,8 @@ describe('rdme changelogs', () => { .basicAuth({ user: key }) .reply(201, { slug: doc.data.slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }); - await expect(changelogs.run({ folder: './__tests__/__fixtures__/slug-docs', key })).resolves.toBe( - "🌱 successfully created 'marc-actually-wrote-a-test' with contents from __tests__/__fixtures__/slug-docs/new-doc-slug.md" + await expect(changelogs.run({ folder: `./__tests__/${fixturesBaseDir}/slug-docs`, key })).resolves.toBe( + `🌱 successfully created 'marc-actually-wrote-a-test' with contents from __tests__/${fixturesBaseDir}/slug-docs/new-doc-slug.md` ); getMock.done(); @@ -367,8 +370,8 @@ describe('rdme changelogs:single', () => { describe('new changelogs', () => { it('should create new changelog', async () => { const slug = 'new-doc'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); const getMock = getApiNock() .get(`/api/v1/changelogs/${slug}`) @@ -386,9 +389,9 @@ describe('rdme changelogs:single', () => { .reply(201, { slug, body: doc.content, ...doc.data }); await expect( - changelogsSingle.run({ filePath: './__tests__/__fixtures__/new-docs/new-doc.md', key }) + changelogsSingle.run({ filePath: `./__tests__/${fixturesBaseDir}/new-docs/new-doc.md`, key }) ).resolves.toBe( - "🌱 successfully created 'new-doc' with contents from ./__tests__/__fixtures__/new-docs/new-doc.md" + `🌱 successfully created 'new-doc' with contents from ./__tests__/${fixturesBaseDir}/new-docs/new-doc.md` ); getMock.done(); @@ -397,7 +400,7 @@ describe('rdme changelogs:single', () => { it('should return creation info for dry run', async () => { const slug = 'new-doc'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/new-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/new-docs/${slug}.md`))); const getMock = getApiNock() .get(`/api/v1/changelogs/${slug}`) @@ -410,9 +413,9 @@ describe('rdme changelogs:single', () => { }); await expect( - changelogsSingle.run({ dryRun: true, filePath: './__tests__/__fixtures__/new-docs/new-doc.md', key }) + changelogsSingle.run({ dryRun: true, filePath: `./__tests__/${fixturesBaseDir}/new-docs/new-doc.md`, key }) ).resolves.toBe( - `🎭 dry run! This will create 'new-doc' with contents from ./__tests__/__fixtures__/new-docs/new-doc.md with the following metadata: ${JSON.stringify( + `🎭 dry run! This will create 'new-doc' with contents from ./__tests__/${fixturesBaseDir}/new-docs/new-doc.md with the following metadata: ${JSON.stringify( doc.data )}` ); @@ -432,9 +435,9 @@ describe('rdme changelogs:single', () => { help: "If you need help, email support@readme.io and include the following link to your API log: 'fake-metrics-uuid'.", }; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/${folder}/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/${folder}/${slug}.md`))); const getMock = getApiNock() .get(`/api/v1/changelogs/${slug}`) @@ -451,7 +454,7 @@ describe('rdme changelogs:single', () => { .basicAuth({ user: key }) .reply(400, errorObject); - const filePath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; const formattedErrorObject = { ...errorObject, @@ -478,7 +481,7 @@ describe('rdme changelogs:single', () => { const getMock = getApiNock().get(`/api/v1/changelogs/${slug}`).basicAuth({ user: key }).reply(500, errorObject); - const filePath = './__tests__/__fixtures__/failure-docs/fail-doc.md'; + const filePath = `./__tests__/${fixturesBaseDir}/failure-docs/fail-doc.md`; const formattedErrorObject = { ...errorObject, @@ -496,8 +499,8 @@ describe('rdme changelogs:single', () => { describe('slug metadata', () => { it('should use provided slug', async () => { const slug = 'new-doc-slug'; - const doc = frontMatter(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); - const hash = hashFileContents(fs.readFileSync(path.join(fixturesDir, `/slug-docs/${slug}.md`))); + const doc = frontMatter(fs.readFileSync(path.join(fullFixturesDir, `/slug-docs/${slug}.md`))); + const hash = hashFileContents(fs.readFileSync(path.join(fullFixturesDir, `/slug-docs/${slug}.md`))); const getMock = getApiNock() .get(`/api/v1/changelogs/${doc.data.slug}`) @@ -515,9 +518,9 @@ describe('rdme changelogs:single', () => { .reply(201, { slug: doc.data.slug, body: doc.content, ...doc.data, lastUpdatedHash: hash }); await expect( - changelogsSingle.run({ filePath: './__tests__/__fixtures__/slug-docs/new-doc-slug.md', key }) + changelogsSingle.run({ filePath: `./__tests__/${fixturesBaseDir}/slug-docs/new-doc-slug.md`, key }) ).resolves.toBe( - "🌱 successfully created 'marc-actually-wrote-a-test' with contents from ./__tests__/__fixtures__/slug-docs/new-doc-slug.md" + `🌱 successfully created 'marc-actually-wrote-a-test' with contents from ./__tests__/${fixturesBaseDir}/slug-docs/new-doc-slug.md` ); getMock.done(); @@ -529,7 +532,7 @@ describe('rdme changelogs:single', () => { let simpleDoc; beforeEach(() => { - const fileContents = fs.readFileSync(path.join(fixturesDir, '/existing-docs/simple-doc.md')); + const fileContents = fs.readFileSync(path.join(fullFixturesDir, '/existing-docs/simple-doc.md')); simpleDoc = { slug: 'simple-doc', doc: frontMatter(fileContents), @@ -557,10 +560,10 @@ describe('rdme changelogs:single', () => { }); return changelogsSingle - .run({ filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key }) + .run({ filePath: `./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, key }) .then(updatedDocs => { expect(updatedDocs).toBe( - "✏️ successfully updated 'simple-doc' with contents from ./__tests__/__fixtures__/existing-docs/simple-doc.md" + `✏️ successfully updated 'simple-doc' with contents from ./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md` ); getMock.done(); @@ -577,13 +580,13 @@ describe('rdme changelogs:single', () => { .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: 'anOldHash' }); return changelogsSingle - .run({ dryRun: true, filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key }) + .run({ dryRun: true, filePath: `./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, key }) .then(updatedDocs => { // All changelogs should have been updated because their hashes from the GET request were different from what they // are currently. expect(updatedDocs).toBe( [ - `🎭 dry run! This will update 'simple-doc' with contents from ./__tests__/__fixtures__/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( + `🎭 dry run! This will update 'simple-doc' with contents from ./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md with the following metadata: ${JSON.stringify( simpleDoc.doc.data )}`, ].join('\n') @@ -602,7 +605,7 @@ describe('rdme changelogs:single', () => { .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: simpleDoc.hash }); return changelogsSingle - .run({ filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key }) + .run({ filePath: `./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, key }) .then(skippedDocs => { expect(skippedDocs).toBe('`simple-doc` was not updated because there were no changes.'); @@ -617,7 +620,7 @@ describe('rdme changelogs:single', () => { .reply(200, { slug: simpleDoc.slug, lastUpdatedHash: simpleDoc.hash }); return changelogsSingle - .run({ dryRun: true, filePath: './__tests__/__fixtures__/existing-docs/simple-doc.md', key }) + .run({ dryRun: true, filePath: `./__tests__/${fixturesBaseDir}/existing-docs/simple-doc.md`, key }) .then(skippedDocs => { expect(skippedDocs).toBe('🎭 dry run! `simple-doc` will not be updated because there were no changes.');