From 276eeeb5755eb7a3c390f7721fabbd674e7e8d63 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 13:21:50 -0600 Subject: [PATCH 01/15] test(openapi/validate): rewrite tests to be more comprehensive --- .../__snapshots__/validate.test.ts.snap | 179 ++++++++++++++++-- __tests__/commands/openapi/validate.test.ts | 88 +++------ 2 files changed, 191 insertions(+), 76 deletions(-) diff --git a/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap b/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap index f9660ddd5..b49f8b6fb 100644 --- a/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap +++ b/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap @@ -1,13 +1,37 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`rdme openapi validate > CI tests > should fail if user attempts to pass \`--github\` flag in CI environment 1`] = ` +[Error: Parsing --github + The \`--github\` flag is only for usage in non-CI environments. +See more help with --help] +`; + +exports[`rdme openapi validate > CI tests > should successfully validate prompt and not run GHA onboarding 1`] = ` +{ + "result": "__tests__/__fixtures__/petstore-simple-weird-version.json is a valid OpenAPI API definition!", + "stderr": "- Validating the API definition located at __tests__/__fixtures__/petstore-simple-weird-version.json... +", + "stdout": "", +} +`; + exports[`rdme openapi validate > GHA onboarding E2E tests > should create GHA workflow if user passes in spec via opt (github flag enabled) 1`] = ` -" +{ + "result": " Your GitHub Actions workflow file has been created! ✨ Almost done! Push your newly created file (.github/workflows/validate-test-opt-spec-github-file.yml) to GitHub and you're all set 🚀 🦉 If you have any more questions, feel free to drop us a line! support@readme.io -" +", + "stderr": "- Validating the API definition located at __tests__/__fixtures__/petstore-simple-weird-version.json... +", + "stdout": "__tests__/__fixtures__/petstore-simple-weird-version.json is a valid OpenAPI API definition! + +🚀 Let's get you set up with GitHub Actions! 🚀 + +", +} `; exports[`rdme openapi validate > GHA onboarding E2E tests > should create GHA workflow if user passes in spec via opt (github flag enabled) 2`] = ` @@ -38,13 +62,26 @@ jobs: `; exports[`rdme openapi validate > GHA onboarding E2E tests > should create GHA workflow if user passes in spec via opt (including workingDirectory) 1`] = ` -" +{ + "result": " Your GitHub Actions workflow file has been created! ✨ Almost done! Push your newly created file (.github/workflows/validate-test-opt-spec-workdir-file.yml) to GitHub and you're all set 🚀 🦉 If you have any more questions, feel free to drop us a line! support@readme.io -" +", + "stderr": "- Validating the API definition located at petstore.json... +", + "stdout": "petstore.json is a valid OpenAPI API definition! + +🐙 Looks like you're running this command in a GitHub Repository! 🐙 + +🚀 With a few quick clicks, you can run this \`openapi validate\` command via GitHub Actions (https://github.com/features/actions) + +✨ This means it will run automagically with every push to a branch of your choice! + +", +} `; exports[`rdme openapi validate > GHA onboarding E2E tests > should create GHA workflow if user passes in spec via opt (including workingDirectory) 2`] = ` @@ -75,13 +112,26 @@ jobs: `; exports[`rdme openapi validate > GHA onboarding E2E tests > should create GHA workflow if user passes in spec via opt 1`] = ` -" +{ + "result": " Your GitHub Actions workflow file has been created! ✨ Almost done! Push your newly created file (.github/workflows/validate-test-opt-spec-file.yml) to GitHub and you're all set 🚀 🦉 If you have any more questions, feel free to drop us a line! support@readme.io -" +", + "stderr": "- Validating the API definition located at __tests__/__fixtures__/petstore-simple-weird-version.json... +", + "stdout": "__tests__/__fixtures__/petstore-simple-weird-version.json is a valid OpenAPI API definition! + +🐙 Looks like you're running this command in a GitHub Repository! 🐙 + +🚀 With a few quick clicks, you can run this \`openapi validate\` command via GitHub Actions (https://github.com/features/actions) + +✨ This means it will run automagically with every push to a branch of your choice! + +", +} `; exports[`rdme openapi validate > GHA onboarding E2E tests > should create GHA workflow if user passes in spec via opt 2`] = ` @@ -112,13 +162,27 @@ jobs: `; exports[`rdme openapi validate > GHA onboarding E2E tests > should create GHA workflow if user passes in spec via prompts 1`] = ` -" +{ + "result": " Your GitHub Actions workflow file has been created! ✨ Almost done! Push your newly created file (.github/workflows/validate-test-file.yml) to GitHub and you're all set 🚀 🦉 If you have any more questions, feel free to drop us a line! support@readme.io -" +", + "stderr": "- Looking for API definitions... +- Validating the API definition located at __tests__/__fixtures__/petstore-simple-weird-version.json... +", + "stdout": "__tests__/__fixtures__/petstore-simple-weird-version.json is a valid OpenAPI API definition! + +🐙 Looks like you're running this command in a GitHub Repository! 🐙 + +🚀 With a few quick clicks, you can run this \`openapi validate\` command via GitHub Actions (https://github.com/features/actions) + +✨ This means it will run automagically with every push to a branch of your choice! + +", +} `; exports[`rdme openapi validate > GHA onboarding E2E tests > should create GHA workflow if user passes in spec via prompts 2`] = ` @@ -148,8 +212,27 @@ jobs: " `; +exports[`rdme openapi validate > GHA onboarding E2E tests > should reject if user says no to creating GHA workflow 1`] = ` +{ + "error": [Error: GitHub Actions workflow creation cancelled. If you ever change your mind, you can run this command again with the \`--github\` flag.], + "stderr": "- Looking for API definitions... +- Validating the API definition located at __tests__/__fixtures__/petstore-simple-weird-version.json... +", + "stdout": "__tests__/__fixtures__/petstore-simple-weird-version.json is a valid OpenAPI API definition! + +🐙 Looks like you're running this command in a GitHub Repository! 🐙 + +🚀 With a few quick clicks, you can run this \`openapi validate\` command via GitHub Actions (https://github.com/features/actions) + +✨ This means it will run automagically with every push to a branch of your choice! + +", +} +`; + exports[`rdme openapi validate > error handling > should throw an error if an invalid API definition has many errors 1`] = ` -[SyntaxError: OpenAPI schema validation failed. +{ + "error": [SyntaxError: OpenAPI schema validation failed. REQUIRED must have required property 'url' @@ -169,13 +252,27 @@ ADDITIONAL PROPERTY must NOT have additional properties | ^^^^^^^ tagss is not expected to be here! 27 | "pet" 28 | ], - 29 | "summary": "Finds Pets by status",] + 29 | "summary": "Finds Pets by status",], + "stderr": "- Validating the API definition located at ./__tests__/__fixtures__/very-invalid-oas.json... +✖ Validating the API definition located at ./__tests__/__fixtures__/very-invalid-oas.json... +", + "stdout": "", +} `; -exports[`rdme openapi validate > error handling > should throw an error if an invalid OpenAPI 3.0 definition is supplied 1`] = `[MissingPointerError: Token "Error" does not exist.]`; +exports[`rdme openapi validate > error handling > should throw an error if an invalid OpenAPI 3.0 definition is supplied 1`] = ` +{ + "error": [MissingPointerError: Token "Error" does not exist.], + "stderr": "- Validating the API definition located at ./__tests__/__fixtures__/invalid-oas.json... +✖ Validating the API definition located at ./__tests__/__fixtures__/invalid-oas.json... +", + "stdout": "", +} +`; exports[`rdme openapi validate > error handling > should throw an error if an invalid OpenAPI 3.1 definition is supplied 1`] = ` -[SyntaxError: OpenAPI schema validation failed. +{ + "error": [SyntaxError: OpenAPI schema validation failed. REQUIRED must have required property 'name' @@ -185,11 +282,17 @@ REQUIRED must have required property 'name' | ^ name is missing here! 27 | "type": "mutualTLS" 28 | } - 29 | }] + 29 | }], + "stderr": "- Validating the API definition located at ./__tests__/__fixtures__/invalid-oas-3.1.json... +✖ Validating the API definition located at ./__tests__/__fixtures__/invalid-oas-3.1.json... +", + "stdout": "", +} `; exports[`rdme openapi validate > error handling > should throw an error if an invalid Swagger definition is supplied 1`] = ` -[SyntaxError: Swagger schema validation failed. +{ + "error": [SyntaxError: Swagger schema validation failed. ADDITIONAL PROPERTY must NOT have additional properties @@ -199,5 +302,51 @@ ADDITIONAL PROPERTY must NOT have additional properties | ^^^^^^^^^^^^^^^^^^^^^^^ this-shouldnt-be-here is not expected to be here! 26 | } 27 | } - 28 | ],] + 28 | ],], + "stderr": "- Validating the API definition located at ./__tests__/__fixtures__/invalid-swagger.json... +✖ Validating the API definition located at ./__tests__/__fixtures__/invalid-swagger.json... +", + "stdout": "", +} +`; + +exports[`rdme openapi validate > error handling > should throw an error if invalid JSON is supplied 1`] = ` +{ + "error": [SyntaxError: Unexpected end of JSON input], + "stderr": "- Validating the API definition located at ./__tests__/__fixtures__/invalid-json/yikes.json... +✖ Validating the API definition located at ./__tests__/__fixtures__/invalid-json/yikes.json... +", + "stdout": "", +} +`; + +exports[`rdme openapi validate > should adhere to .gitignore in subdirectories 1`] = ` +{ + "result": "nest/petstore.json is a valid OpenAPI API definition!", + "stderr": "- Looking for API definitions... +- Validating the API definition located at nest/petstore.json... +", + "stdout": "ℹ️ We found nest/petstore.json and are attempting to validate it. +", +} +`; + +exports[`rdme openapi validate > should discover and upload an API definition if none is provided 1`] = ` +{ + "result": "petstore.json is a valid OpenAPI API definition!", + "stderr": "- Looking for API definitions... +- Validating the API definition located at petstore.json... +", + "stdout": "ℹ️ We found petstore.json and are attempting to validate it. +", +} +`; + +exports[`rdme openapi validate > should use specified working directory 1`] = ` +{ + "result": "petstore.json is a valid OpenAPI API definition!", + "stderr": "- Validating the API definition located at petstore.json... +", + "stdout": "", +} `; diff --git a/__tests__/commands/openapi/validate.test.ts b/__tests__/commands/openapi/validate.test.ts index aa54bc9ec..03f71d59c 100644 --- a/__tests__/commands/openapi/validate.test.ts +++ b/__tests__/commands/openapi/validate.test.ts @@ -1,37 +1,27 @@ -/* eslint-disable no-console */ + import fs from 'node:fs'; -import chalk from 'chalk'; import prompts from 'prompts'; -import { describe, beforeAll, beforeEach, afterEach, it, expect, vi, type MockInstance } from 'vitest'; +import { describe, beforeAll, beforeEach, afterEach, it, expect, vi } from 'vitest'; import Command from '../../../src/commands/openapi/validate.js'; import { after, before } from '../../helpers/get-gha-setup.js'; -import { runCommandAndReturnResult, runCommandWithHooks } from '../../helpers/oclif.js'; - -let consoleInfoSpy: MockInstance; - -const getCommandOutput = () => { - return [consoleInfoSpy.mock.calls.join('\n\n')].filter(Boolean).join('\n\n'); -}; +import { runCommand, runCommandWithHooks, type OclifOutput } from '../../helpers/oclif.js'; describe('rdme openapi validate', () => { - let run: (args?: string[]) => Promise; + let run: (args?: string[]) => OclifOutput; let testWorkingDir: string; beforeAll(() => { - run = runCommandAndReturnResult(Command); + run = runCommand(Command); }); beforeEach(() => { - consoleInfoSpy = vi.spyOn(console, 'info').mockImplementation(() => {}); testWorkingDir = process.cwd(); }); afterEach(() => { - consoleInfoSpy.mockRestore(); - process.chdir(testWorkingDir); }); @@ -42,66 +32,53 @@ describe('rdme openapi validate', () => { ['OpenAPI 3.0', 'yaml', '3.0'], ['OpenAPI 3.1', 'json', '3.1'], ['OpenAPI 3.1', 'yaml', '3.1'], - ])('should support validating a %s definition (format: %s)', (_, format, specVersion) => { - expect(console.info).toHaveBeenCalledTimes(0); - - return expect( - run([require.resolve(`@readme/oas-examples/${specVersion}/${format}/petstore.${format}`)]), - ).resolves.toContain( + ])('should support validating a %s definition (format: %s)', async (_, format, specVersion) => { + expect( + (await run([require.resolve(`@readme/oas-examples/${specVersion}/${format}/petstore.${format}`)])).result, + ).toContain( `petstore.${format} is a valid ${specVersion === '2.0' ? 'Swagger' : 'OpenAPI'} API definition!`, ); }); it('should discover and upload an API definition if none is provided', async () => { - await expect(run(['--workingDirectory', './__tests__/__fixtures__/relative-ref-oas'])).resolves.toBe( - chalk.green('petstore.json is a valid OpenAPI API definition!'), - ); - - expect(console.info).toHaveBeenCalledTimes(1); - - const output = getCommandOutput(); - - expect(output).toBe(chalk.yellow('ℹ️ We found petstore.json and are attempting to validate it.')); + const result = await run(['--workingDirectory', './__tests__/__fixtures__/relative-ref-oas']); + expect(result).toMatchSnapshot(); }); - it('should use specified working directory', () => { - return expect( - run(['petstore.json', '--workingDirectory', './__tests__/__fixtures__/relative-ref-oas']), - ).resolves.toBe(chalk.green('petstore.json is a valid OpenAPI API definition!')); + it('should use specified working directory', async () => { + const result = await run(['petstore.json', '--workingDirectory', './__tests__/__fixtures__/relative-ref-oas']); + expect(result).toMatchSnapshot(); }); - it('should adhere to .gitignore in subdirectories', () => { + it('should adhere to .gitignore in subdirectories', async () => { fs.copyFileSync( require.resolve('@readme/oas-examples/3.0/json/petstore-simple.json'), './__tests__/__fixtures__/nested-gitignored-oas/nest/petstore-ignored.json', ); - return expect(run(['--workingDirectory', './__tests__/__fixtures__/nested-gitignored-oas'])).resolves.toBe( - chalk.green('nest/petstore.json is a valid OpenAPI API definition!'), - ); + const result = await run(['--workingDirectory', './__tests__/__fixtures__/nested-gitignored-oas']); + expect(result).toMatchSnapshot(); }); describe('error handling', () => { it('should throw an error if invalid JSON is supplied', () => { - return expect(run(['./__tests__/__fixtures__/invalid-json/yikes.json'])).rejects.toStrictEqual( - new SyntaxError('Unexpected end of JSON input'), - ); + return expect(run(['./__tests__/__fixtures__/invalid-json/yikes.json'])).resolves.toMatchSnapshot(); }); it('should throw an error if an invalid OpenAPI 3.0 definition is supplied', () => { - return expect(run(['./__tests__/__fixtures__/invalid-oas.json'])).rejects.toMatchSnapshot(); + return expect(run(['./__tests__/__fixtures__/invalid-oas.json'])).resolves.toMatchSnapshot(); }); it('should throw an error if an invalid OpenAPI 3.1 definition is supplied', () => { - return expect(run(['./__tests__/__fixtures__/invalid-oas-3.1.json'])).rejects.toMatchSnapshot(); + return expect(run(['./__tests__/__fixtures__/invalid-oas-3.1.json'])).resolves.toMatchSnapshot(); }); it('should throw an error if an invalid Swagger definition is supplied', () => { - return expect(run(['./__tests__/__fixtures__/invalid-swagger.json'])).rejects.toMatchSnapshot(); + return expect(run(['./__tests__/__fixtures__/invalid-swagger.json'])).resolves.toMatchSnapshot(); }); it('should throw an error if an invalid API definition has many errors', () => { - return expect(run(['./__tests__/__fixtures__/very-invalid-oas.json'])).rejects.toMatchSnapshot(); + return expect(run(['./__tests__/__fixtures__/very-invalid-oas.json'])).resolves.toMatchSnapshot(); }); }); @@ -118,7 +95,7 @@ describe('rdme openapi validate', () => { vi.stubEnv('TEST_RDME_CREATEGHA', 'true'); const spec = '__tests__/__fixtures__/petstore-simple-weird-version.json'; - await expect(run([spec])).resolves.toBe(chalk.green(`${spec} is a valid OpenAPI API definition!`)); + await expect(run([spec])).resolves.toMatchSnapshot(); }); it('should fail if user attempts to pass `--github` flag in CI environment', async () => { @@ -129,8 +106,8 @@ describe('rdme openapi validate', () => { '__tests__/__fixtures__/petstore-simple-weird-version.json', '--github', ]) - ).error.message, - ).toContain('The `--github` flag is only for usage in non-CI environments'); + ).error, + ).toMatchSnapshot(); }); }); @@ -148,22 +125,15 @@ describe('rdme openapi validate', () => { }); it('should create GHA workflow if user passes in spec via prompts', async () => { - expect.assertions(6); + expect.assertions(3); const spec = '__tests__/__fixtures__/petstore-simple-weird-version.json'; const fileName = 'validate-test-file'; prompts.inject([spec, true, 'validate-test-branch', fileName]); await expect(run()).resolves.toMatchSnapshot(); - expect(yamlOutput).toMatchSnapshot(); expect(fs.writeFileSync).toHaveBeenCalledWith(`.github/workflows/${fileName}.yml`, expect.any(String)); - expect(console.info).toHaveBeenCalledTimes(2); - - const output = getCommandOutput(); - - expect(output).toMatch("Looks like you're running this command in a GitHub Repository!"); - expect(output).toMatch('is a valid OpenAPI API definition!'); }); it('should create GHA workflow if user passes in spec via opt', async () => { @@ -210,11 +180,7 @@ describe('rdme openapi validate', () => { it('should reject if user says no to creating GHA workflow', () => { const spec = '__tests__/__fixtures__/petstore-simple-weird-version.json'; prompts.inject([spec, false]); - return expect(run()).rejects.toStrictEqual( - new Error( - 'GitHub Actions workflow creation cancelled. If you ever change your mind, you can run this command again with the `--github` flag.', - ), - ); + return expect(run()).resolves.toMatchSnapshot(); }); }); }); From 43760164544f42413f35a143b6e3dd7983e0c625 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 13:36:16 -0600 Subject: [PATCH 02/15] chore: prettier --- __tests__/commands/openapi/validate.test.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/__tests__/commands/openapi/validate.test.ts b/__tests__/commands/openapi/validate.test.ts index 03f71d59c..1812e355f 100644 --- a/__tests__/commands/openapi/validate.test.ts +++ b/__tests__/commands/openapi/validate.test.ts @@ -1,5 +1,3 @@ - - import fs from 'node:fs'; import prompts from 'prompts'; @@ -35,9 +33,7 @@ describe('rdme openapi validate', () => { ])('should support validating a %s definition (format: %s)', async (_, format, specVersion) => { expect( (await run([require.resolve(`@readme/oas-examples/${specVersion}/${format}/petstore.${format}`)])).result, - ).toContain( - `petstore.${format} is a valid ${specVersion === '2.0' ? 'Swagger' : 'OpenAPI'} API definition!`, - ); + ).toContain(`petstore.${format} is a valid ${specVersion === '2.0' ? 'Swagger' : 'OpenAPI'} API definition!`); }); it('should discover and upload an API definition if none is provided', async () => { From d2c6c7d6c97a591444875240a08c4fb25ae2e7ed Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 13:40:38 -0600 Subject: [PATCH 03/15] chore: remove `expect.assertions` calls --- __tests__/commands/openapi/validate.test.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/__tests__/commands/openapi/validate.test.ts b/__tests__/commands/openapi/validate.test.ts index 1812e355f..995e97cb8 100644 --- a/__tests__/commands/openapi/validate.test.ts +++ b/__tests__/commands/openapi/validate.test.ts @@ -121,8 +121,6 @@ describe('rdme openapi validate', () => { }); it('should create GHA workflow if user passes in spec via prompts', async () => { - expect.assertions(3); - const spec = '__tests__/__fixtures__/petstore-simple-weird-version.json'; const fileName = 'validate-test-file'; prompts.inject([spec, true, 'validate-test-branch', fileName]); @@ -133,8 +131,6 @@ describe('rdme openapi validate', () => { }); it('should create GHA workflow if user passes in spec via opt', async () => { - expect.assertions(3); - const spec = '__tests__/__fixtures__/petstore-simple-weird-version.json'; const fileName = 'validate-test-opt-spec-file'; prompts.inject([true, 'validate-test-opt-spec-branch', fileName]); @@ -146,8 +142,6 @@ describe('rdme openapi validate', () => { }); it('should create GHA workflow if user passes in spec via opt (including workingDirectory)', async () => { - expect.assertions(3); - const spec = 'petstore.json'; const fileName = 'validate-test-opt-spec-workdir-file'; prompts.inject([true, 'validate-test-opt-spec-github-branch', fileName]); @@ -161,8 +155,6 @@ describe('rdme openapi validate', () => { }); it('should create GHA workflow if user passes in spec via opt (github flag enabled)', async () => { - expect.assertions(3); - const spec = '__tests__/__fixtures__/petstore-simple-weird-version.json'; const fileName = 'validate-test-opt-spec-github-file'; prompts.inject(['validate-test-opt-spec-github-branch', fileName]); From 0a9c7372ac8762e56680105944564442fa7027fc Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 13:55:26 -0600 Subject: [PATCH 04/15] test: convert --- .../__snapshots__/convert.test.ts.snap | 41 +++++++++++++++++++ __tests__/commands/openapi/convert.test.ts | 37 +++++++++-------- 2 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 __tests__/commands/openapi/__snapshots__/convert.test.ts.snap diff --git a/__tests__/commands/openapi/__snapshots__/convert.test.ts.snap b/__tests__/commands/openapi/__snapshots__/convert.test.ts.snap new file mode 100644 index 000000000..e2bdd6924 --- /dev/null +++ b/__tests__/commands/openapi/__snapshots__/convert.test.ts.snap @@ -0,0 +1,41 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`rdme openapi convert > error handling > should fail if given an OpenAPI 3.0 definition (format: json) 1`] = ` +{ + "result": "Your API definition has been converted and bundled and saved to output.json!", + "stderr": "- Validating the API definition located at /Users/kanadg/Code/readmeio/rdme/node_modules/@readme/oas-examples/3.0/json/petstore.json... +⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled. +", + "stdout": "", +} +`; + +exports[`rdme openapi convert > error handling > should fail if given an OpenAPI 3.0 definition (format: yaml) 1`] = ` +{ + "result": "Your API definition has been converted and bundled and saved to output.json!", + "stderr": "- Validating the API definition located at /Users/kanadg/Code/readmeio/rdme/node_modules/@readme/oas-examples/3.0/yaml/petstore.yaml... +⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled. +", + "stdout": "", +} +`; + +exports[`rdme openapi convert > error handling > should warn if given an OpenAPI 3.0 definition (format: json) 1`] = ` +{ + "result": "Your API definition has been converted and bundled and saved to output.json!", + "stderr": "- Validating the API definition located at petstore.json... +⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled. +", + "stdout": "", +} +`; + +exports[`rdme openapi convert > error handling > should warn if given an OpenAPI 3.0 definition (format: yaml) 1`] = ` +{ + "result": "Your API definition has been converted and bundled and saved to output.json!", + "stderr": "- Validating the API definition located at petstore.yaml... +⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled. +", + "stdout": "", +} +`; diff --git a/__tests__/commands/openapi/convert.test.ts b/__tests__/commands/openapi/convert.test.ts index 023f7cded..30695199b 100644 --- a/__tests__/commands/openapi/convert.test.ts +++ b/__tests__/commands/openapi/convert.test.ts @@ -6,18 +6,18 @@ import prompts from 'prompts'; import { describe, it, expect, vi, beforeAll, beforeEach, afterEach, type MockInstance } from 'vitest'; import Command from '../../../src/commands/openapi/convert.js'; -import { runCommandAndReturnResult } from '../../helpers/oclif.js'; +import { runCommand, type OclifOutput } from '../../helpers/oclif.js'; const successfulConversion = () => 'Your API definition has been converted and bundled and saved to output.json!'; describe('rdme openapi convert', () => { let fsWriteFileSyncSpy: MockInstance; let reducedSpec: OASDocument; - let run: (args?: string[]) => Promise; + let run: (args?: string[]) => OclifOutput; let testWorkingDir: string; beforeAll(() => { - run = runCommandAndReturnResult(Command); + run = runCommand(Command); }); beforeEach(() => { @@ -42,7 +42,7 @@ describe('rdme openapi convert', () => { prompts.inject(['output.json']); - await expect(run([spec])).resolves.toBe(successfulConversion()); + expect((await run([spec])).result).toBe(successfulConversion()); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(reducedSpec.tags).toHaveLength(1); @@ -62,7 +62,14 @@ describe('rdme openapi convert', () => { '--out', 'output.json', ]), - ).resolves.toBe(successfulConversion()); + ).resolves.toMatchInlineSnapshot(` + { + "result": "Your API definition has been converted and bundled and saved to output.json!", + "stderr": "- Validating the API definition located at petstore-simple.json... + ", + "stdout": "", + } + `); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(Object.keys(reducedSpec.paths)).toStrictEqual(['/pet/{petId}']); @@ -70,14 +77,18 @@ describe('rdme openapi convert', () => { }); describe('error handling', () => { - it.each([['json'], ['yaml']])('should fail if given an OpenAPI 3.0 definition (format: %s)', async format => { - const spec = require.resolve(`@readme/oas-examples/3.0/${format}/petstore.${format}`); - - const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + it.each([['json'], ['yaml']])('should warn if given an OpenAPI 3.0 definition (format: %s)', async format => { + const spec = `petstore.${format}`; prompts.inject(['output.json']); - await expect(run([spec])).resolves.toBe(successfulConversion()); + await expect( + run([ + spec, + '--workingDirectory', + require.resolve(`@readme/oas-examples/3.0/${format}/${spec}`).replace(spec, ''), + ]), + ).resolves.toMatchSnapshot(); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(reducedSpec.tags).toHaveLength(3); @@ -98,12 +109,6 @@ describe('rdme openapi convert', () => { '/user/{username}', ]); expect(Object.keys(reducedSpec.paths['/pet/{petId}'])).toStrictEqual(['get', 'post', 'delete']); - - expect(consoleWarnSpy).toHaveBeenCalledWith( - '⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled.', - ); - - consoleWarnSpy.mockRestore(); }); }); }); From e73245899c8a18d509ee1956e1530c180e9e437e Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 14:02:37 -0600 Subject: [PATCH 05/15] chore: redo convert again --- .../__snapshots__/convert.test.ts.snap | 23 +++++-------------- __tests__/commands/openapi/convert.test.ts | 15 ++++-------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/__tests__/commands/openapi/__snapshots__/convert.test.ts.snap b/__tests__/commands/openapi/__snapshots__/convert.test.ts.snap index e2bdd6924..eee6c4f98 100644 --- a/__tests__/commands/openapi/__snapshots__/convert.test.ts.snap +++ b/__tests__/commands/openapi/__snapshots__/convert.test.ts.snap @@ -1,40 +1,29 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`rdme openapi convert > error handling > should fail if given an OpenAPI 3.0 definition (format: json) 1`] = ` -{ - "result": "Your API definition has been converted and bundled and saved to output.json!", - "stderr": "- Validating the API definition located at /Users/kanadg/Code/readmeio/rdme/node_modules/@readme/oas-examples/3.0/json/petstore.json... -⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled. -", - "stdout": "", -} -`; - -exports[`rdme openapi convert > error handling > should fail if given an OpenAPI 3.0 definition (format: yaml) 1`] = ` +exports[`rdme openapi convert > error handling > should warn if given an OpenAPI 3.0 definition (format: json) 1`] = ` { "result": "Your API definition has been converted and bundled and saved to output.json!", - "stderr": "- Validating the API definition located at /Users/kanadg/Code/readmeio/rdme/node_modules/@readme/oas-examples/3.0/yaml/petstore.yaml... + "stderr": "- Validating the API definition located at petstore.json... ⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled. ", "stdout": "", } `; -exports[`rdme openapi convert > error handling > should warn if given an OpenAPI 3.0 definition (format: json) 1`] = ` +exports[`rdme openapi convert > error handling > should warn if given an OpenAPI 3.0 definition (format: yaml) 1`] = ` { "result": "Your API definition has been converted and bundled and saved to output.json!", - "stderr": "- Validating the API definition located at petstore.json... + "stderr": "- Validating the API definition located at petstore.yaml... ⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled. ", "stdout": "", } `; -exports[`rdme openapi convert > error handling > should warn if given an OpenAPI 3.0 definition (format: yaml) 1`] = ` +exports[`rdme openapi convert > should convert with no prompts via opts 1`] = ` { "result": "Your API definition has been converted and bundled and saved to output.json!", - "stderr": "- Validating the API definition located at petstore.yaml... -⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled. + "stderr": "- Validating the API definition located at petstore-simple.json... ", "stdout": "", } diff --git a/__tests__/commands/openapi/convert.test.ts b/__tests__/commands/openapi/convert.test.ts index 30695199b..534eb8e98 100644 --- a/__tests__/commands/openapi/convert.test.ts +++ b/__tests__/commands/openapi/convert.test.ts @@ -8,8 +8,6 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterEach, type MockIn import Command from '../../../src/commands/openapi/convert.js'; import { runCommand, type OclifOutput } from '../../helpers/oclif.js'; -const successfulConversion = () => 'Your API definition has been converted and bundled and saved to output.json!'; - describe('rdme openapi convert', () => { let fsWriteFileSyncSpy: MockInstance; let reducedSpec: OASDocument; @@ -42,7 +40,9 @@ describe('rdme openapi convert', () => { prompts.inject(['output.json']); - expect((await run([spec])).result).toBe(successfulConversion()); + expect((await run([spec])).result).toBe( + 'Your API definition has been converted and bundled and saved to output.json!', + ); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(reducedSpec.tags).toHaveLength(1); @@ -62,14 +62,7 @@ describe('rdme openapi convert', () => { '--out', 'output.json', ]), - ).resolves.toMatchInlineSnapshot(` - { - "result": "Your API definition has been converted and bundled and saved to output.json!", - "stderr": "- Validating the API definition located at petstore-simple.json... - ", - "stdout": "", - } - `); + ).resolves.toMatchSnapshot(); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(Object.keys(reducedSpec.paths)).toStrictEqual(['/pet/{petId}']); From e1757901655f6771c5f0510c731e4dab5394a888 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 14:20:44 -0600 Subject: [PATCH 06/15] test: inspect --- .../__snapshots__/inspect.test.ts.snap | 21 ++++++++---- __tests__/commands/openapi/inspect.test.ts | 32 ++++++------------- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap b/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap index 3ad9461f7..6a3110ac1 100644 --- a/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap +++ b/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap @@ -51,7 +51,7 @@ polymorphism: `; exports[`rdme openapi inspect > feature reports > should generate a report for '@readme/oas-examples/3.0/json/readme-…' (w/ [ 'readme' ]) 1`] = ` -" +[SoftError: x-default: You do not use this. x-readme.code-samples: · #/paths/~1x-code-samples/get/x-code-samples @@ -73,7 +73,7 @@ x-readme.samples-languages: · node · python · shell - · swift" + · swift] `; exports[`rdme openapi inspect > feature reports > should generate a report for '@readme/oas-examples/3.0/json/schema-…' (w/ [ 'additionalProperties', …(1) ]) 1`] = ` @@ -98,7 +98,7 @@ circularRefs: `; exports[`rdme openapi inspect > feature reports > should generate a report for '@readme/oas-examples/3.0/json/schema-…' (w/ [ 'additionalProperties', …(2) ]) 1`] = ` -" +[SoftError: additionalProperties: · #/components/schemas/BodyPart/properties/headers/additionalProperties · #/components/schemas/BodyPart/properties/mediaType/properties/parameters/additionalProperties @@ -122,11 +122,11 @@ x-readme.code-samples: You do not use this. x-readme.headers: You do not use this. x-readme.explorer-enabled: You do not use this. x-readme.proxy-enabled: You do not use this. -x-readme.samples-languages: You do not use this." +x-readme.samples-languages: You do not use this.] `; exports[`rdme openapi inspect > feature reports > should generate a report for '@readme/oas-examples/3.0/json/schema-…' (w/ [ 'circularRefs', 'readme' ]) 1`] = ` -" +[SoftError: circularRefs: · #/components/schemas/MultiPart/properties/parent · #/components/schemas/ZoneOffset/properties/rules @@ -136,7 +136,7 @@ x-readme.code-samples: You do not use this. x-readme.headers: You do not use this. x-readme.explorer-enabled: You do not use this. x-readme.proxy-enabled: You do not use this. -x-readme.samples-languages: You do not use this." +x-readme.samples-languages: You do not use this.] `; exports[`rdme openapi inspect > feature reports > should generate a report for '@readme/oas-examples/3.1/json/train-t…' (w/ [ 'commonParameters' ]) 1`] = ` @@ -146,6 +146,15 @@ commonParameters: · #/paths/~1bookings~1{bookingId}~1payment/parameters" `; +exports[`rdme openapi inspect > feature reports > should throw an error if an invalid feature is supplied 1`] = ` +{ + "error": [Error: Expected --feature=reamde to be one of: additionalProperties, callbacks, circularRefs, commonParameters, discriminators, links, style, polymorphism, serverVariables, webhooks, xml, readme +See more help with --help], + "stderr": "", + "stdout": "", +} +`; + exports[`rdme openapi inspect > full reports > should generate a report for @readme/oas-examples/3.0/json/petstore.json 1`] = ` "Here are some interesting things we found in your API definition. 🕵️ diff --git a/__tests__/commands/openapi/inspect.test.ts b/__tests__/commands/openapi/inspect.test.ts index 4ac04244d..236bc5630 100644 --- a/__tests__/commands/openapi/inspect.test.ts +++ b/__tests__/commands/openapi/inspect.test.ts @@ -1,16 +1,14 @@ /* eslint-disable @vitest/no-conditional-expect */ -import assert from 'node:assert'; - import { describe, it, expect, beforeAll } from 'vitest'; import Command from '../../../src/commands/openapi/inspect.js'; -import { runCommandAndReturnResult } from '../../helpers/oclif.js'; +import { runCommand, type OclifOutput } from '../../helpers/oclif.js'; describe('rdme openapi inspect', () => { - let run: (args?: string[]) => Promise; + let run: (args?: string[]) => OclifOutput; beforeAll(() => { - run = runCommandAndReturnResult(Command); + run = runCommand(Command); }); describe('full reports', () => { @@ -19,18 +17,15 @@ describe('rdme openapi inspect', () => { '@readme/oas-examples/3.0/json/readme.json', '@readme/oas-examples/3.0/json/readme-extensions.json', '@readme/oas-examples/3.1/json/train-travel.json', - ])('should generate a report for %s', spec => { - return expect(run([require.resolve(spec)])).resolves.toMatchSnapshot(); + ])('should generate a report for %s', async spec => { + expect((await run([require.resolve(spec)])).result).toMatchSnapshot(); }); }); describe('feature reports', () => { it('should throw an error if an invalid feature is supplied', () => { const spec = require.resolve('@readme/oas-examples/3.0/json/readme-extensions.json'); - - return expect(run([spec, '--feature', 'style', '--feature', 'reamde'])).rejects.toThrow( - 'Expected --feature=reamde to be one of:', - ); + return expect(run([spec, '--feature', 'style', '--feature', 'reamde'])).resolves.toMatchSnapshot(); }); const cases: { feature: string[]; shouldSoftError?: true; spec: string }[] = [ @@ -68,18 +63,9 @@ describe('rdme openapi inspect', () => { it.each(cases)('should generate a report for $spec (w/ $feature)', async ({ spec, feature, shouldSoftError }) => { const args = [require.resolve(spec)].concat(...feature.map(f => ['--feature', f])); if (!shouldSoftError) { - await expect(run(args)).resolves.toMatchSnapshot(); - - return; - } - - try { - await run(args); - - assert.fail('A soft error should have been thrown for this test case.'); - } catch (err) { - expect(err.name).toBe('SoftError'); - expect(err.message).toMatchSnapshot(); + expect((await run(args)).result).toMatchSnapshot(); + } else { + expect((await run(args)).error).toMatchSnapshot(); } }); }); From 57efb58a05735fe6eeed2088bf111768fecf96df Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 14:41:37 -0600 Subject: [PATCH 07/15] chore: slight rework of how we deconstruct result --- __tests__/commands/openapi/convert.test.ts | 5 ++--- __tests__/commands/openapi/inspect.test.ts | 8 +++++--- __tests__/commands/openapi/validate.test.ts | 7 ++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/__tests__/commands/openapi/convert.test.ts b/__tests__/commands/openapi/convert.test.ts index 534eb8e98..8c73637e6 100644 --- a/__tests__/commands/openapi/convert.test.ts +++ b/__tests__/commands/openapi/convert.test.ts @@ -40,9 +40,8 @@ describe('rdme openapi convert', () => { prompts.inject(['output.json']); - expect((await run([spec])).result).toBe( - 'Your API definition has been converted and bundled and saved to output.json!', - ); + const { result } = await run([spec]); + expect(result).toBe('Your API definition has been converted and bundled and saved to output.json!'); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(reducedSpec.tags).toHaveLength(1); diff --git a/__tests__/commands/openapi/inspect.test.ts b/__tests__/commands/openapi/inspect.test.ts index 236bc5630..e1152a923 100644 --- a/__tests__/commands/openapi/inspect.test.ts +++ b/__tests__/commands/openapi/inspect.test.ts @@ -18,7 +18,8 @@ describe('rdme openapi inspect', () => { '@readme/oas-examples/3.0/json/readme-extensions.json', '@readme/oas-examples/3.1/json/train-travel.json', ])('should generate a report for %s', async spec => { - expect((await run([require.resolve(spec)])).result).toMatchSnapshot(); + const { result } = await run([require.resolve(spec)]); + expect(result).toMatchSnapshot(); }); }); @@ -62,10 +63,11 @@ describe('rdme openapi inspect', () => { it.each(cases)('should generate a report for $spec (w/ $feature)', async ({ spec, feature, shouldSoftError }) => { const args = [require.resolve(spec)].concat(...feature.map(f => ['--feature', f])); + const { result, error } = await run(args); if (!shouldSoftError) { - expect((await run(args)).result).toMatchSnapshot(); + expect(result).toMatchSnapshot(); } else { - expect((await run(args)).error).toMatchSnapshot(); + expect(error).toMatchSnapshot(); } }); }); diff --git a/__tests__/commands/openapi/validate.test.ts b/__tests__/commands/openapi/validate.test.ts index 995e97cb8..c732ce458 100644 --- a/__tests__/commands/openapi/validate.test.ts +++ b/__tests__/commands/openapi/validate.test.ts @@ -31,9 +31,10 @@ describe('rdme openapi validate', () => { ['OpenAPI 3.1', 'json', '3.1'], ['OpenAPI 3.1', 'yaml', '3.1'], ])('should support validating a %s definition (format: %s)', async (_, format, specVersion) => { - expect( - (await run([require.resolve(`@readme/oas-examples/${specVersion}/${format}/petstore.${format}`)])).result, - ).toContain(`petstore.${format} is a valid ${specVersion === '2.0' ? 'Swagger' : 'OpenAPI'} API definition!`); + const { result } = await run([require.resolve(`@readme/oas-examples/${specVersion}/${format}/petstore.${format}`)]); + expect(result).toContain( + `petstore.${format} is a valid ${specVersion === '2.0' ? 'Swagger' : 'OpenAPI'} API definition!`, + ); }); it('should discover and upload an API definition if none is provided', async () => { From a7b77fb0db36c6b1f014f536a2015fd4736faed2 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 14:45:56 -0600 Subject: [PATCH 08/15] docs: deprecate --- __tests__/helpers/oclif.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/__tests__/helpers/oclif.ts b/__tests__/helpers/oclif.ts index d422010ba..a8db897a9 100644 --- a/__tests__/helpers/oclif.ts +++ b/__tests__/helpers/oclif.ts @@ -50,6 +50,8 @@ export function runCommand(Command: CommandClass) { * an error if the command throws one. Mainly a helper to minimize the amount of refactoring * in our existing tests. * + * @deprecated This is a legacy helper to aid with the initial migration to `oclif`. + * Use `runCommand` for all new tests instead. * @example runCommandAndReturnResult(LoginCommand)(['--email', 'owlbert@example.com', '--password', 'password']) */ export function runCommandAndReturnResult(Command: CommandClass) { From e15f7d7f196d15ad811071d7760e248556cd2979 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 14:46:26 -0600 Subject: [PATCH 09/15] test: reduce --- .../openapi/__snapshots__/reduce.test.ts.snap | 67 +++++++++++++ __tests__/commands/openapi/reduce.test.ts | 94 +++++-------------- 2 files changed, 92 insertions(+), 69 deletions(-) create mode 100644 __tests__/commands/openapi/__snapshots__/reduce.test.ts.snap diff --git a/__tests__/commands/openapi/__snapshots__/reduce.test.ts.snap b/__tests__/commands/openapi/__snapshots__/reduce.test.ts.snap new file mode 100644 index 000000000..75c85bc8a --- /dev/null +++ b/__tests__/commands/openapi/__snapshots__/reduce.test.ts.snap @@ -0,0 +1,67 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`rdme openapi reduce > error handling > should fail if given a Swagger 2.0 definition (format: json) 1`] = `[Error: Sorry, this reducer feature in rdme only supports OpenAPI 3.0+ definitions.]`; + +exports[`rdme openapi reduce > error handling > should fail if given a Swagger 2.0 definition (format: yaml) 1`] = `[Error: Sorry, this reducer feature in rdme only supports OpenAPI 3.0+ definitions.]`; + +exports[`rdme openapi reduce > error handling > should fail if you attempt to pass both tags and methods as opts 1`] = `[Error: You can pass in either tags or paths/methods, but not both.]`; + +exports[`rdme openapi reduce > error handling > should fail if you attempt to pass both tags and paths as opts 1`] = `[Error: You can pass in either tags or paths/methods, but not both.]`; + +exports[`rdme openapi reduce > error handling > should fail if you attempt to pass non-existent path and no method 1`] = `[Error: All paths in the API definition were removed. Did you supply the right path name to reduce by?]`; + +exports[`rdme openapi reduce > error handling > should fail if you attempt to reduce a spec to nothing via paths 1`] = `[Error: All paths in the API definition were removed. Did you supply the right path name to reduce by?]`; + +exports[`rdme openapi reduce > error handling > should fail if you attempt to reduce a spec to nothing via tags 1`] = `[Error: All paths in the API definition were removed. Did you supply the right path name to reduce by?]`; + +exports[`rdme openapi reduce > reducing > by path > should reduce and update title with no prompts via opts 1`] = ` +{ + "result": "Your reduced API definition has been saved to output.json! 🤏", + "stderr": "- Looking for API definitions... +- Validating the API definition located at petstore.json... +- Reducing your API definition... +✔ Reducing your API definition... done! ✅ +", + "stdout": "ℹ️ We found petstore.json and are attempting to reduce it. +", +} +`; + +exports[`rdme openapi reduce > reducing > by path > should reduce with no prompts via opts 1`] = ` +{ + "result": "Your reduced API definition has been saved to output.json! 🤏", + "stderr": "- Looking for API definitions... +- Validating the API definition located at petstore.json... +- Reducing your API definition... +✔ Reducing your API definition... done! ✅ +", + "stdout": "ℹ️ We found petstore.json and are attempting to reduce it. +", +} +`; + +exports[`rdme openapi reduce > reducing > by tag > should discover and upload an API definition if none is provided 1`] = ` +{ + "result": "Your reduced API definition has been saved to output.json! 🤏", + "stderr": "- Looking for API definitions... +- Validating the API definition located at petstore.json... +- Reducing your API definition... +✔ Reducing your API definition... done! ✅ +", + "stdout": "ℹ️ We found petstore.json and are attempting to reduce it. +", +} +`; + +exports[`rdme openapi reduce > reducing > by tag > should reduce with no prompts via opts 1`] = ` +{ + "result": "Your reduced API definition has been saved to output.json! 🤏", + "stderr": "- Looking for API definitions... +- Validating the API definition located at petstore.json... +- Reducing your API definition... +✔ Reducing your API definition... done! ✅ +", + "stdout": "ℹ️ We found petstore.json and are attempting to reduce it. +", +} +`; diff --git a/__tests__/commands/openapi/reduce.test.ts b/__tests__/commands/openapi/reduce.test.ts index 21e75bc3f..e6d259404 100644 --- a/__tests__/commands/openapi/reduce.test.ts +++ b/__tests__/commands/openapi/reduce.test.ts @@ -1,32 +1,24 @@ -/* eslint-disable no-console */ import type { OASDocument } from 'oas/types'; import fs from 'node:fs'; -import chalk from 'chalk'; import prompts from 'prompts'; import { describe, beforeAll, beforeEach, afterEach, it, expect, vi, type MockInstance } from 'vitest'; import Command from '../../../src/commands/openapi/reduce.js'; -import { runCommandAndReturnResult } from '../../helpers/oclif.js'; - -const successfulReduction = () => 'Your reduced API definition has been saved to output.json! 🤏'; - -let consoleInfoSpy: MockInstance; -const getCommandOutput = () => consoleInfoSpy.mock.calls.join('\n\n'); +import { runCommand, type OclifOutput } from '../../helpers/oclif.js'; describe('rdme openapi reduce', () => { let fsWriteFileSyncSpy: MockInstance; let reducedSpec: OASDocument; - let run: (args?: string[]) => Promise; + let run: (args?: string[]) => OclifOutput; let testWorkingDir: string; beforeAll(() => { - run = runCommandAndReturnResult(Command); + run = runCommand(Command); }); beforeEach(() => { - consoleInfoSpy = vi.spyOn(console, 'info').mockImplementation(() => {}); testWorkingDir = process.cwd(); fsWriteFileSyncSpy = vi.spyOn(fs, 'writeFileSync').mockImplementationOnce((filename, data) => { reducedSpec = JSON.parse(data as string); @@ -34,7 +26,6 @@ describe('rdme openapi reduce', () => { }); afterEach(() => { - consoleInfoSpy.mockRestore(); process.chdir(testWorkingDir); vi.restoreAllMocks(); }); @@ -51,7 +42,8 @@ describe('rdme openapi reduce', () => { prompts.inject(['tags', ['pet'], 'output.json']); - await expect(run([spec])).resolves.toBe(successfulReduction()); + const { result } = await run([spec]); + expect(result).toBe('Your reduced API definition has been saved to output.json! 🤏'); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(reducedSpec.tags).toHaveLength(1); @@ -65,26 +57,16 @@ describe('rdme openapi reduce', () => { }); it('should discover and upload an API definition if none is provided', async () => { - const spec = 'petstore.json'; - prompts.inject(['tags', ['user'], 'output.json']); - await expect(run(['--workingDirectory', './__tests__/__fixtures__/relative-ref-oas'])).resolves.toBe( - successfulReduction(), - ); - - expect(console.info).toHaveBeenCalledTimes(1); - - const output = getCommandOutput(); - - expect(output).toBe(chalk.yellow(`ℹ️ We found ${spec} and are attempting to reduce it.`)); + await expect( + run(['--workingDirectory', './__tests__/__fixtures__/relative-ref-oas']), + ).resolves.toMatchSnapshot(); expect(Object.keys(reducedSpec.paths)).toStrictEqual(['/user']); }); it('should reduce with no prompts via opts', async () => { - const spec = 'petstore.json'; - await expect( run([ '--workingDirectory', @@ -94,13 +76,7 @@ describe('rdme openapi reduce', () => { '--out', 'output.json', ]), - ).resolves.toBe(successfulReduction()); - - expect(console.info).toHaveBeenCalledTimes(1); - - const output = getCommandOutput(); - - expect(output).toBe(chalk.yellow(`ℹ️ We found ${spec} and are attempting to reduce it.`)); + ).resolves.toMatchSnapshot(); expect(Object.keys(reducedSpec.paths)).toStrictEqual(['/user']); }); @@ -117,7 +93,8 @@ describe('rdme openapi reduce', () => { prompts.inject(['paths', ['/pet', '/pet/findByStatus'], ['get', 'post'], 'output.json']); - await expect(run([spec])).resolves.toBe(successfulReduction()); + const { result } = await run([spec]); + expect(result).toBe('Your reduced API definition has been saved to output.json! 🤏'); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(reducedSpec.tags).toHaveLength(1); @@ -127,8 +104,6 @@ describe('rdme openapi reduce', () => { }); it('should reduce with no prompts via opts', async () => { - const spec = 'petstore.json'; - await expect( run([ '--workingDirectory', @@ -144,13 +119,7 @@ describe('rdme openapi reduce', () => { '--out', 'output.json', ]), - ).resolves.toBe(successfulReduction()); - - expect(console.info).toHaveBeenCalledTimes(1); - - const output = getCommandOutput(); - - expect(output).toBe(chalk.yellow(`ℹ️ We found ${spec} and are attempting to reduce it.`)); + ).resolves.toMatchSnapshot(); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(Object.keys(reducedSpec.paths)).toStrictEqual(['/pet', '/pet/{petId}']); @@ -159,7 +128,6 @@ describe('rdme openapi reduce', () => { }); it('should reduce and update title with no prompts via opts', async () => { - const spec = 'petstore.json'; const title = 'some alternative title'; await expect( @@ -179,13 +147,7 @@ describe('rdme openapi reduce', () => { '--out', 'output.json', ]), - ).resolves.toBe(successfulReduction()); - - expect(console.info).toHaveBeenCalledTimes(1); - - const output = getCommandOutput(); - - expect(output).toBe(chalk.yellow(`ℹ️ We found ${spec} and are attempting to reduce it.`)); + ).resolves.toMatchSnapshot(); expect(fsWriteFileSyncSpy).toHaveBeenCalledWith('output.json', expect.any(String)); expect(Object.keys(reducedSpec.paths)).toStrictEqual(['/pet', '/pet/{petId}']); @@ -200,9 +162,8 @@ describe('rdme openapi reduce', () => { it.each([['json'], ['yaml']])('should fail if given a Swagger 2.0 definition (format: %s)', async format => { const spec = require.resolve(`@readme/oas-examples/2.0/${format}/petstore.${format}`); - await expect(run([spec])).rejects.toStrictEqual( - new Error('Sorry, this reducer feature in rdme only supports OpenAPI 3.0+ definitions.'), - ); + const { error } = await run([spec]); + expect(error).toMatchSnapshot(); }); it('should fail if you attempt to reduce a spec to nothing via tags', async () => { @@ -210,9 +171,8 @@ describe('rdme openapi reduce', () => { prompts.inject(['tags', ['unknown-tag'], 'output.json']); - await expect(run([spec])).rejects.toStrictEqual( - new Error('All paths in the API definition were removed. Did you supply the right path name to reduce by?'), - ); + const { error } = await run([spec]); + expect(error).toMatchSnapshot(); }); it('should fail if you attempt to reduce a spec to nothing via paths', async () => { @@ -220,33 +180,29 @@ describe('rdme openapi reduce', () => { prompts.inject(['paths', ['unknown-path'], 'output.json']); - await expect(run([spec])).rejects.toStrictEqual( - new Error('All paths in the API definition were removed. Did you supply the right path name to reduce by?'), - ); + const { error } = await run([spec]); + expect(error).toMatchSnapshot(); }); it('should fail if you attempt to pass both tags and paths as opts', async () => { const spec = require.resolve('@readme/oas-examples/3.0/json/petstore.json'); - await expect(run([spec, '--tag', 'tag1', '--tag', 'tag2', '--path', '/path'])).rejects.toStrictEqual( - new Error('You can pass in either tags or paths/methods, but not both.'), - ); + const { error } = await run([spec, '--tag', 'tag1', '--tag', 'tag2', '--path', '/path']); + expect(error).toMatchSnapshot(); }); it('should fail if you attempt to pass both tags and methods as opts', async () => { const spec = require.resolve('@readme/oas-examples/3.0/json/petstore.json'); - await expect(run([spec, '--tag', 'tag1', '--tag', 'tag2', '--method', 'get'])).rejects.toStrictEqual( - new Error('You can pass in either tags or paths/methods, but not both.'), - ); + const { error } = await run([spec, '--tag', 'tag1', '--tag', 'tag2', '--method', 'get']); + expect(error).toMatchSnapshot(); }); it('should fail if you attempt to pass non-existent path and no method', async () => { const spec = require.resolve('@readme/oas-examples/3.0/json/petstore.json'); - await expect(run([spec, '--path', 'unknown-path'])).rejects.toStrictEqual( - new Error('All paths in the API definition were removed. Did you supply the right path name to reduce by?'), - ); + const { error } = await run([spec, '--path', '/unknown-path']); + expect(error).toMatchSnapshot(); }); }); }); From 79f23bbbea6817df27f9abe5d7375269775fd90d Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 14:54:10 -0600 Subject: [PATCH 10/15] test: login --- .../commands/__snapshots__/login.test.ts.snap | 77 +++++++++++++++++++ __tests__/commands/login.test.ts | 32 ++++---- 2 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 __tests__/commands/__snapshots__/login.test.ts.snap diff --git a/__tests__/commands/__snapshots__/login.test.ts.snap b/__tests__/commands/__snapshots__/login.test.ts.snap new file mode 100644 index 000000000..8c2112941 --- /dev/null +++ b/__tests__/commands/__snapshots__/login.test.ts.snap @@ -0,0 +1,77 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`rdme login > should bypass prompts and post to /login on the API if passing in every opt (no 2FA) 1`] = ` +{ + "result": "Successfully logged in as user@example.com to the subdomain project.", + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme login > should bypass prompts and post to /login on the API if passing in every opt 1`] = ` +{ + "result": "Successfully logged in as user@example.com to the subdomain project.", + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme login > should error if email is invalid 1`] = ` +{ + "error": [Error: You must provide a valid email address.], + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme login > should error if invalid credentials are given 1`] = ` +{ + "error": [APIv1Error: Either your email address or password is incorrect + +If you need help, email support@readme.io and mention log "fake-metrics-uuid".], + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme login > should error if no project provided 1`] = ` +{ + "error": [Error: No project subdomain provided. Please use \`--project\`.], + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme login > should error if trying to access a project that is not yours 1`] = ` +{ + "error": [APIv1Error: The project (unauthorized-project) can't be found. + +If you need help, email support@readme.io], + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme login > should make additional prompt for token if login requires 2FA 1`] = ` +{ + "result": "Successfully logged in as user@example.com to the subdomain project.", + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme login > should post to /login on the API 1`] = ` +{ + "result": "Successfully logged in as user@example.com to the subdomain project.", + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme login > should post to /login on the API if passing in project via opt 1`] = ` +{ + "result": "Successfully logged in as user@example.com to the subdomain project.", + "stderr": "", + "stdout": "", +} +`; diff --git a/__tests__/commands/login.test.ts b/__tests__/commands/login.test.ts index 67173a200..bacbab0a2 100644 --- a/__tests__/commands/login.test.ts +++ b/__tests__/commands/login.test.ts @@ -5,7 +5,7 @@ import Command from '../../src/commands/login.js'; import { APIv1Error } from '../../src/lib/apiError.js'; import configStore from '../../src/lib/configstore.js'; import { getAPIv1Mock } from '../helpers/get-api-mock.js'; -import { runCommandAndReturnResult } from '../helpers/oclif.js'; +import { runCommand, type OclifOutput } from '../helpers/oclif.js'; const apiKey = 'abcdefg'; const email = 'user@example.com'; @@ -14,22 +14,22 @@ const project = 'subdomain'; const token = '123456'; describe('rdme login', () => { - let run: (args?: string[]) => Promise; + let run: (args?: string[]) => OclifOutput; beforeAll(() => { - run = runCommandAndReturnResult(Command); + run = runCommand(Command); }); afterEach(() => configStore.clear()); it('should error if no project provided', () => { prompts.inject([email, password]); - return expect(run()).rejects.toStrictEqual(new Error('No project subdomain provided. Please use `--project`.')); + return expect(run()).resolves.toMatchSnapshot(); }); it('should error if email is invalid', () => { prompts.inject(['this-is-not-an-email', password, project]); - return expect(run()).rejects.toStrictEqual(new Error('You must provide a valid email address.')); + return expect(run()).resolves.toMatchSnapshot(); }); it('should post to /login on the API', async () => { @@ -37,7 +37,7 @@ describe('rdme login', () => { const mock = getAPIv1Mock().post('/api/v1/login', { email, password, project }).reply(200, { apiKey }); - await expect(run()).resolves.toBe('Successfully logged in as user@example.com to the subdomain project.'); + await expect(run()).resolves.toMatchSnapshot(); mock.done(); @@ -51,9 +51,7 @@ describe('rdme login', () => { const mock = getAPIv1Mock().post('/api/v1/login', { email, password, project }).reply(200, { apiKey }); - await expect(run(['--project', project])).resolves.toBe( - 'Successfully logged in as user@example.com to the subdomain project.', - ); + await expect(run(['--project', project])).resolves.toMatchSnapshot(); mock.done(); @@ -65,9 +63,9 @@ describe('rdme login', () => { it('should bypass prompts and post to /login on the API if passing in every opt', async () => { const mock = getAPIv1Mock().post('/api/v1/login', { email, password, project, token }).reply(200, { apiKey }); - await expect(run(['--email', email, '--password', password, '--project', project, '--otp', token])).resolves.toBe( - 'Successfully logged in as user@example.com to the subdomain project.', - ); + await expect( + run(['--email', email, '--password', password, '--project', project, '--otp', token]), + ).resolves.toMatchSnapshot(); mock.done(); @@ -79,9 +77,7 @@ describe('rdme login', () => { it('should bypass prompts and post to /login on the API if passing in every opt (no 2FA)', async () => { const mock = getAPIv1Mock().post('/api/v1/login', { email, password, project }).reply(200, { apiKey }); - await expect(run(['--email', email, '--password', password, '--project', project])).resolves.toBe( - 'Successfully logged in as user@example.com to the subdomain project.', - ); + await expect(run(['--email', email, '--password', password, '--project', project])).resolves.toMatchSnapshot(); mock.done(); @@ -101,7 +97,7 @@ describe('rdme login', () => { const mock = getAPIv1Mock().post('/api/v1/login', { email, password, project }).reply(401, errorResponse); - await expect(run()).rejects.toStrictEqual(new APIv1Error(errorResponse)); + await expect(run()).resolves.toMatchSnapshot(); mock.done(); }); @@ -121,7 +117,7 @@ describe('rdme login', () => { .post('/api/v1/login', { email, password, project, token }) .reply(200, { apiKey }); - await expect(run()).resolves.toBe('Successfully logged in as user@example.com to the subdomain project.'); + await expect(run()).resolves.toMatchSnapshot(); mock.done(); @@ -144,7 +140,7 @@ describe('rdme login', () => { .post('/api/v1/login', { email, password, project: projectThatIsNotYours }) .reply(404, errorResponse); - await expect(run()).rejects.toStrictEqual(new APIv1Error(errorResponse)); + await expect(run()).resolves.toMatchSnapshot(); mock.done(); }); From 5298d38d5a53c2cdb22ccd17da9791929ac6d570 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 14:55:56 -0600 Subject: [PATCH 11/15] test: logout --- .../commands/__snapshots__/logout.test.ts.snap | 17 +++++++++++++++++ __tests__/commands/logout.test.ts | 14 +++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 __tests__/commands/__snapshots__/logout.test.ts.snap diff --git a/__tests__/commands/__snapshots__/logout.test.ts.snap b/__tests__/commands/__snapshots__/logout.test.ts.snap new file mode 100644 index 000000000..a7756dc5d --- /dev/null +++ b/__tests__/commands/__snapshots__/logout.test.ts.snap @@ -0,0 +1,17 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`rdme logout > should log the user out 1`] = ` +{ + "result": "You have logged out of ReadMe. Please use \`rdme login\` to login again.", + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme logout > should report the user as logged out if they aren't logged in 1`] = ` +{ + "result": "You have logged out of ReadMe. Please use \`rdme login\` to login again.", + "stderr": "", + "stdout": "", +} +`; diff --git a/__tests__/commands/logout.test.ts b/__tests__/commands/logout.test.ts index 901e7dea9..7ded1c16b 100644 --- a/__tests__/commands/logout.test.ts +++ b/__tests__/commands/logout.test.ts @@ -3,13 +3,13 @@ import { describe, afterEach, beforeAll, it, expect } from 'vitest'; import pkg from '../../package.json' with { type: 'json' }; import Command from '../../src/commands/logout.js'; import configStore from '../../src/lib/configstore.js'; -import { runCommandAndReturnResult } from '../helpers/oclif.js'; +import { runCommand, type OclifOutput } from '../helpers/oclif.js'; describe('rdme logout', () => { - let run: (args?: string[]) => Promise; + let run: (args?: string[]) => OclifOutput; beforeAll(() => { - run = runCommandAndReturnResult(Command); + run = runCommand(Command); }); afterEach(() => { @@ -20,18 +20,14 @@ describe('rdme logout', () => { configStore.delete('email'); configStore.delete('project'); - return expect(run()).resolves.toBe( - `You have logged out of ReadMe. Please use \`${pkg.name} login\` to login again.`, - ); + return expect(run()).resolves.toMatchSnapshot(); }); it('should log the user out', async () => { configStore.set('email', 'email@example.com'); configStore.set('project', 'subdomain'); - await expect(run()).resolves.toBe( - `You have logged out of ReadMe. Please use \`${pkg.name} login\` to login again.`, - ); + await expect(run()).resolves.toMatchSnapshot(); expect(configStore.get('email')).toBeUndefined(); expect(configStore.get('project')).toBeUndefined(); From e2d7e26995761a6d9f95650fcbc12cfc23f92664 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 14:57:47 -0600 Subject: [PATCH 12/15] test: whoami --- .../commands/__snapshots__/whoami.test.ts.snap | 17 +++++++++++++++++ __tests__/commands/whoami.test.ts | 11 +++++------ 2 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 __tests__/commands/__snapshots__/whoami.test.ts.snap diff --git a/__tests__/commands/__snapshots__/whoami.test.ts.snap b/__tests__/commands/__snapshots__/whoami.test.ts.snap new file mode 100644 index 000000000..143714935 --- /dev/null +++ b/__tests__/commands/__snapshots__/whoami.test.ts.snap @@ -0,0 +1,17 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`rdme whoami > should error if user is not authenticated 1`] = ` +{ + "error": [Error: Please login using \`rdme login\`.], + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme whoami > should return the authenticated user 1`] = ` +{ + "result": "You are currently logged in as email@example.com to the subdomain project.", + "stderr": "", + "stdout": "", +} +`; diff --git a/__tests__/commands/whoami.test.ts b/__tests__/commands/whoami.test.ts index aecd0bbee..f7fcdfb47 100644 --- a/__tests__/commands/whoami.test.ts +++ b/__tests__/commands/whoami.test.ts @@ -1,15 +1,14 @@ import { describe, afterEach, it, expect, beforeAll } from 'vitest'; -import pkg from '../../package.json' with { type: 'json' }; import Command from '../../src/commands/whoami.js'; import configStore from '../../src/lib/configstore.js'; -import { runCommandAndReturnResult } from '../helpers/oclif.js'; +import { runCommand, type OclifOutput } from '../helpers/oclif.js'; describe('rdme whoami', () => { - let run: (args?: string[]) => Promise; + let run: (args?: string[]) => OclifOutput; beforeAll(() => { - run = runCommandAndReturnResult(Command); + run = runCommand(Command); }); afterEach(() => { @@ -20,13 +19,13 @@ describe('rdme whoami', () => { configStore.delete('email'); configStore.delete('project'); - return expect(run()).rejects.toStrictEqual(new Error(`Please login using \`${pkg.name} login\`.`)); + return expect(run()).resolves.toMatchSnapshot(); }); it('should return the authenticated user', () => { configStore.set('email', 'email@example.com'); configStore.set('project', 'subdomain'); - return expect(run()).resolves.toBe('You are currently logged in as email@example.com to the subdomain project.'); + return expect(run()).resolves.toMatchSnapshot(); }); }); From 759f6d3b58db4ceb439cc9d339b755a5a02f6c0d Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 15:04:49 -0600 Subject: [PATCH 13/15] chore: lint --- __tests__/commands/login.test.ts | 1 - __tests__/commands/logout.test.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/__tests__/commands/login.test.ts b/__tests__/commands/login.test.ts index bacbab0a2..601f47899 100644 --- a/__tests__/commands/login.test.ts +++ b/__tests__/commands/login.test.ts @@ -2,7 +2,6 @@ import prompts from 'prompts'; import { describe, beforeAll, afterEach, it, expect } from 'vitest'; import Command from '../../src/commands/login.js'; -import { APIv1Error } from '../../src/lib/apiError.js'; import configStore from '../../src/lib/configstore.js'; import { getAPIv1Mock } from '../helpers/get-api-mock.js'; import { runCommand, type OclifOutput } from '../helpers/oclif.js'; diff --git a/__tests__/commands/logout.test.ts b/__tests__/commands/logout.test.ts index 7ded1c16b..577cb5bf5 100644 --- a/__tests__/commands/logout.test.ts +++ b/__tests__/commands/logout.test.ts @@ -1,6 +1,5 @@ import { describe, afterEach, beforeAll, it, expect } from 'vitest'; -import pkg from '../../package.json' with { type: 'json' }; import Command from '../../src/commands/logout.js'; import configStore from '../../src/lib/configstore.js'; import { runCommand, type OclifOutput } from '../helpers/oclif.js'; From 2a9e39acf52436c9d0cee26f3903a2d95cb33814 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 15:16:45 -0600 Subject: [PATCH 14/15] test: snaps --- .../openapi/__snapshots__/inspect.test.ts.snap | 17 +---------------- .../openapi/__snapshots__/validate.test.ts.snap | 10 ++++------ 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap b/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap index ffd23ec8a..6a3110ac1 100644 --- a/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap +++ b/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap @@ -93,13 +93,8 @@ additionalProperties: · #/components/schemas/ParameterizedHeader/properties/parameters/additionalProperties circularRefs: - · #/components/schemas/BodyPart/properties/parent - · #/components/schemas/MultiPart/properties/bodyParts/items · #/components/schemas/MultiPart/properties/parent - · #/components/schemas/ZoneOffset/properties/rules - · #/components/schemas/ZoneOffsetTransition/properties/offsetAfter - · #/components/schemas/ZoneOffsetTransition/properties/offsetBefore - · #/components/schemas/ZoneRules/properties/transitions/items" + · #/components/schemas/ZoneOffset/properties/rules" `; exports[`rdme openapi inspect > feature reports > should generate a report for '@readme/oas-examples/3.0/json/schema-…' (w/ [ 'additionalProperties', …(2) ]) 1`] = ` @@ -119,13 +114,8 @@ additionalProperties: · #/components/schemas/ParameterizedHeader/properties/parameters/additionalProperties circularRefs: - · #/components/schemas/BodyPart/properties/parent - · #/components/schemas/MultiPart/properties/bodyParts/items · #/components/schemas/MultiPart/properties/parent · #/components/schemas/ZoneOffset/properties/rules - · #/components/schemas/ZoneOffsetTransition/properties/offsetAfter - · #/components/schemas/ZoneOffsetTransition/properties/offsetBefore - · #/components/schemas/ZoneRules/properties/transitions/items x-default: You do not use this. x-readme.code-samples: You do not use this. @@ -138,13 +128,8 @@ x-readme.samples-languages: You do not use this.] exports[`rdme openapi inspect > feature reports > should generate a report for '@readme/oas-examples/3.0/json/schema-…' (w/ [ 'circularRefs', 'readme' ]) 1`] = ` [SoftError: circularRefs: - · #/components/schemas/BodyPart/properties/parent - · #/components/schemas/MultiPart/properties/bodyParts/items · #/components/schemas/MultiPart/properties/parent · #/components/schemas/ZoneOffset/properties/rules - · #/components/schemas/ZoneOffsetTransition/properties/offsetAfter - · #/components/schemas/ZoneOffsetTransition/properties/offsetBefore - · #/components/schemas/ZoneRules/properties/transitions/items x-default: You do not use this. x-readme.code-samples: You do not use this. diff --git a/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap b/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap index 56083ed60..b49f8b6fb 100644 --- a/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap +++ b/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap @@ -232,7 +232,7 @@ exports[`rdme openapi validate > GHA onboarding E2E tests > should reject if use exports[`rdme openapi validate > error handling > should throw an error if an invalid API definition has many errors 1`] = ` { - "error": [ValidationError: OpenAPI schema validation failed. + "error": [SyntaxError: OpenAPI schema validation failed. REQUIRED must have required property 'url' @@ -262,9 +262,7 @@ ADDITIONAL PROPERTY must NOT have additional properties exports[`rdme openapi validate > error handling > should throw an error if an invalid OpenAPI 3.0 definition is supplied 1`] = ` { - "error": [ValidationError: API definition schema validation failed. - -Missing $ref pointer "#/components/schemas/Error". Token "Error" does not exist.], + "error": [MissingPointerError: Token "Error" does not exist.], "stderr": "- Validating the API definition located at ./__tests__/__fixtures__/invalid-oas.json... ✖ Validating the API definition located at ./__tests__/__fixtures__/invalid-oas.json... ", @@ -274,7 +272,7 @@ Missing $ref pointer "#/components/schemas/Error". Token "Error" does not exist. exports[`rdme openapi validate > error handling > should throw an error if an invalid OpenAPI 3.1 definition is supplied 1`] = ` { - "error": [ValidationError: OpenAPI schema validation failed. + "error": [SyntaxError: OpenAPI schema validation failed. REQUIRED must have required property 'name' @@ -294,7 +292,7 @@ REQUIRED must have required property 'name' exports[`rdme openapi validate > error handling > should throw an error if an invalid Swagger definition is supplied 1`] = ` { - "error": [ValidationError: Swagger schema validation failed. + "error": [SyntaxError: Swagger schema validation failed. ADDITIONAL PROPERTY must NOT have additional properties From da6b18815677ccf7fa55b64d4406a67ba9882844 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 5 Mar 2025 15:19:13 -0600 Subject: [PATCH 15/15] chore: bad merge oops --- .../openapi/__snapshots__/inspect.test.ts.snap | 17 ++++++++++++++++- .../openapi/__snapshots__/validate.test.ts.snap | 10 ++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap b/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap index 6a3110ac1..ffd23ec8a 100644 --- a/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap +++ b/__tests__/commands/openapi/__snapshots__/inspect.test.ts.snap @@ -93,8 +93,13 @@ additionalProperties: · #/components/schemas/ParameterizedHeader/properties/parameters/additionalProperties circularRefs: + · #/components/schemas/BodyPart/properties/parent + · #/components/schemas/MultiPart/properties/bodyParts/items · #/components/schemas/MultiPart/properties/parent - · #/components/schemas/ZoneOffset/properties/rules" + · #/components/schemas/ZoneOffset/properties/rules + · #/components/schemas/ZoneOffsetTransition/properties/offsetAfter + · #/components/schemas/ZoneOffsetTransition/properties/offsetBefore + · #/components/schemas/ZoneRules/properties/transitions/items" `; exports[`rdme openapi inspect > feature reports > should generate a report for '@readme/oas-examples/3.0/json/schema-…' (w/ [ 'additionalProperties', …(2) ]) 1`] = ` @@ -114,8 +119,13 @@ additionalProperties: · #/components/schemas/ParameterizedHeader/properties/parameters/additionalProperties circularRefs: + · #/components/schemas/BodyPart/properties/parent + · #/components/schemas/MultiPart/properties/bodyParts/items · #/components/schemas/MultiPart/properties/parent · #/components/schemas/ZoneOffset/properties/rules + · #/components/schemas/ZoneOffsetTransition/properties/offsetAfter + · #/components/schemas/ZoneOffsetTransition/properties/offsetBefore + · #/components/schemas/ZoneRules/properties/transitions/items x-default: You do not use this. x-readme.code-samples: You do not use this. @@ -128,8 +138,13 @@ x-readme.samples-languages: You do not use this.] exports[`rdme openapi inspect > feature reports > should generate a report for '@readme/oas-examples/3.0/json/schema-…' (w/ [ 'circularRefs', 'readme' ]) 1`] = ` [SoftError: circularRefs: + · #/components/schemas/BodyPart/properties/parent + · #/components/schemas/MultiPart/properties/bodyParts/items · #/components/schemas/MultiPart/properties/parent · #/components/schemas/ZoneOffset/properties/rules + · #/components/schemas/ZoneOffsetTransition/properties/offsetAfter + · #/components/schemas/ZoneOffsetTransition/properties/offsetBefore + · #/components/schemas/ZoneRules/properties/transitions/items x-default: You do not use this. x-readme.code-samples: You do not use this. diff --git a/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap b/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap index b49f8b6fb..56083ed60 100644 --- a/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap +++ b/__tests__/commands/openapi/__snapshots__/validate.test.ts.snap @@ -232,7 +232,7 @@ exports[`rdme openapi validate > GHA onboarding E2E tests > should reject if use exports[`rdme openapi validate > error handling > should throw an error if an invalid API definition has many errors 1`] = ` { - "error": [SyntaxError: OpenAPI schema validation failed. + "error": [ValidationError: OpenAPI schema validation failed. REQUIRED must have required property 'url' @@ -262,7 +262,9 @@ ADDITIONAL PROPERTY must NOT have additional properties exports[`rdme openapi validate > error handling > should throw an error if an invalid OpenAPI 3.0 definition is supplied 1`] = ` { - "error": [MissingPointerError: Token "Error" does not exist.], + "error": [ValidationError: API definition schema validation failed. + +Missing $ref pointer "#/components/schemas/Error". Token "Error" does not exist.], "stderr": "- Validating the API definition located at ./__tests__/__fixtures__/invalid-oas.json... ✖ Validating the API definition located at ./__tests__/__fixtures__/invalid-oas.json... ", @@ -272,7 +274,7 @@ exports[`rdme openapi validate > error handling > should throw an error if an in exports[`rdme openapi validate > error handling > should throw an error if an invalid OpenAPI 3.1 definition is supplied 1`] = ` { - "error": [SyntaxError: OpenAPI schema validation failed. + "error": [ValidationError: OpenAPI schema validation failed. REQUIRED must have required property 'name' @@ -292,7 +294,7 @@ REQUIRED must have required property 'name' exports[`rdme openapi validate > error handling > should throw an error if an invalid Swagger definition is supplied 1`] = ` { - "error": [SyntaxError: Swagger schema validation failed. + "error": [ValidationError: Swagger schema validation failed. ADDITIONAL PROPERTY must NOT have additional properties