From d21a6b4dba810aee9979564b59e98e081759b7c6 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Wed, 19 Jun 2024 18:08:18 +0100 Subject: [PATCH] Update tests --- src/cli.ts | 22 +-- src/helpers/cliHelper.ts | 25 +++ tests/cli.test.ts | 187 ------------------ .../__snapshots__/outputHelper.test.ts.snap | 29 +++ tests/helpers/cliHelper.test.ts | 118 +++++++++++ tests/helpers/outputHelper.test.ts | 99 ++++++++++ 6 files changed, 273 insertions(+), 207 deletions(-) create mode 100644 src/helpers/cliHelper.ts delete mode 100644 tests/cli.test.ts create mode 100644 tests/helpers/__snapshots__/outputHelper.test.ts.snap create mode 100644 tests/helpers/cliHelper.test.ts create mode 100644 tests/helpers/outputHelper.test.ts diff --git a/src/cli.ts b/src/cli.ts index 13ce7f7..0d739e2 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -2,8 +2,7 @@ import { Command, Option } from "commander"; import randomBunny from "./index"; import ICliOptions from "./contracts/ICliOptions"; import { exit } from "process"; -import OutputHelper from "./helpers/outputHelper"; -import { writeFileSync } from "fs"; +import CliHelper from "./helpers/cliHelper"; const program = new Command(); @@ -22,21 +21,4 @@ program.parse(); const options: ICliOptions = program.opts(); randomBunny(options.subreddit, options.sort) - .then((response) => { - if (response.IsSuccess) { - const output = OutputHelper.GenerateOutput(response, options); - - if (options.o) { - writeFileSync(options.o, output); - } else { - console.log(output); - } - - exit(0); - } else { - const error = response.Error!; - - console.error(error.Message, error.Code); - exit(1); - } - }); \ No newline at end of file + .then((response) => exit(CliHelper.Endpoint(response, options))); \ No newline at end of file diff --git a/src/helpers/cliHelper.ts b/src/helpers/cliHelper.ts new file mode 100644 index 0000000..2776f2e --- /dev/null +++ b/src/helpers/cliHelper.ts @@ -0,0 +1,25 @@ +import { writeFileSync } from "fs"; +import ICliOptions from "../contracts/ICliOptions"; +import IReturnResult from "../contracts/IReturnResult"; +import OutputHelper from "./outputHelper"; + +export default class CliHelper { + public static Endpoint(response: IReturnResult, options: ICliOptions): number { + if (response.IsSuccess) { + const output = OutputHelper.GenerateOutput(response, options); + + if (options.o) { + writeFileSync(options.o, output); + } else { + console.log(output); + } + + return 0; + } else { + const error = response.Error!; + + console.error(error.Message, error.Code); + return 1; + } + } +} \ No newline at end of file diff --git a/tests/cli.test.ts b/tests/cli.test.ts deleted file mode 100644 index 66fecbe..0000000 --- a/tests/cli.test.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { exec } from "child_process"; -import path from "path"; - -describe('default', () => { - test('GIVEN no options are supplied, EXPECT standard output', async () => { - const result = await cli([], '.'); - - const keys = result.stdout.split('\n') - .flatMap(x => x.split(' = ')[0]) - .filter(x => x && x.length > 0); - const values = result.stdout.split('\n') - .flatMap(x => x.split(' = ')[1]) - .filter(x => x && x.length > 0); - - - expect(result.code).toBe(0); - expect(keys).toStrictEqual(['Archived', 'Downvotes', 'Hidden', 'Permalink', 'Subreddit', 'Subreddit Subscribers', 'Title', 'Upvotes', 'Url']); - expect(values.length).toBe(9); - }, 5000); - - test('GIVEN an error occurs, EXPECT error output', async () => { - const result = await cli(['-s', 'textonly'], '.'); - - expect(result.code).toBe(1); - expect(result.stderr).toBeDefined(); - }, 5000); -}); - -describe('version', () => { - test('GIVEN -V flag is supplied, EXPECT version returned', async () => { - const result = await cli(['-V'], '.'); - - expect(result.code).toBe(0); - expect(result.stdout).toBe('2.2\n'); - }); - - test('GIVEN --version is supplied, EXPECT version returned', async () => { - const result = await cli(['--version'], '.'); - - expect(result.code).toBe(0); - expect(result.stdout).toBe('2.2\n'); - }); -}); - -describe('help', () => { - test('GIVEN -h is supplied, EXPECT help returned', async () => { - const result = await cli(['-h'], '.'); - - expect(result.code).toBe(0); - expect(result.stdout.split('\n')[0]).toBe('Usage: random-bunny [options]'); - }); - - test('GIVEN --help is supplied, EXPECT help returned', async () => { - const result = await cli(['--help'], '.'); - - expect(result.code).toBe(0); - expect(result.stdout.split('\n')[0]).toBe('Usage: random-bunny [options]'); - }); -}); - -describe('subreddit', () => { - test('GIVEN -s is not supplied, EXPECT subreddit to be defaulted', async () => { - const result = await cli([], '.'); - - const subreddit = result.stdout.split('\n') - .find(x => x && x.length > 0 && x.split(' = ')[0] == 'Subreddit')! - .split(' = ')[1]; - - expect(subreddit).toBe('Rabbits'); - }, 5000); - - test('GIVEN -s is supplied, EXPECT subreddit to be changed', async () => { - const result = await cli(['-s', 'pics'], '.'); - - const subreddit = result.stdout.split('\n') - .find(x => x && x.length > 0 && x.split(' = ')[0] == 'Subreddit')! - .split(' = ')[1]; - - expect(subreddit).toBe('pics'); - }, 5000); - - test('GIVEN --subreddit is supplied, EXPECT subreddit to be changed', async () => { - const result = await cli(['--subreddit', 'pics'], '.'); - - const subreddit = result.stdout.split('\n') - .find(x => x && x.length > 0 && x.split(' = ')[0] == 'Subreddit')! - .split(' = ')[1]; - - expect(subreddit).toBe('pics'); - }, 5000); -}); - -describe('json', () => { - test('GIVEN -j is supplied, EXPECT output to be valid JSON', async () => { - const result = await cli(['-j'], '.'); - - const json = JSON.parse(result.stdout); - - expect(json).toBeDefined(); - }, 5000); - - test('GIVEN --json is supplied, EXPECT output to be valid JSON', async () => { - const result = await cli(['--json'], '.'); - - const json = JSON.parse(result.stdout); - - expect(json).toBeDefined(); - }, 5000); -}); - -describe('sort', () => { - test('GIVEN --sort is not supplied, EXPECT sort to be defaulted', async () => { - const result = await cli(['-q'], '.'); - - const sortBy = result.stdout.split('\n') - .find(x => x && x.length > 0 && x.split(' = ')[0] == 'Query.Sort By')! - .split(' = ')[1]; - - expect(sortBy).toBe('hot'); - }, 5000); - - test('GIVEN --sort is supplied WITH a valid input, EXPECT sort to be used', async () => { - const result = await cli(['-q', '--sort', 'new'], '.'); - - const sortBy = result.stdout.split('\n') - .find(x => x && x.length > 0 && x.split(' = ')[0] == 'Query.Sort By')! - .split(' = ')[1]; - - expect(sortBy).toBe('new'); - }, 5000); - - test('GIVEN --sort is supplied WITH an invalid input, EXPECT error', async () => { - const result = await cli(['-q', '--sort', 'invalid'], '.'); - - expect(result.code).toBe(1); - expect(result.stderr).toBe("error: option '--sort ' argument 'invalid' is invalid. Allowed choices are hot, new, top.\n"); - }, 5000); -}); - -describe('query-metadata', () => { - test('GIVEN --query-metadata is not supplied, EXPECT no query metadata returned', async () => { - const result = await cli([], '.'); - - const query = result.stdout.split('\n') - .find(x => x && x.length > 0 && x.split(' = ')[0].startsWith('Query')); - - expect(query).toBeUndefined(); - }, 5000); - - test('GIVEN --query-metadata is supplied, EXPECT query metadata returned', async () => { - const result = await cli(['--query-metadata'], '.'); - - const query = result.stdout.split('\n') - .find(x => x && x.length > 0 && x.split(' = ')[0].startsWith('Query')); - - expect(query).toBeDefined(); - }, 5000); - - test('GIVEN -q is supplied, EXPECT query metadata returned', async () => { - const result = await cli(['-q'], '.'); - - const query = result.stdout.split('\n') - .find(x => x && x.length > 0 && x.split(' = ')[0].startsWith('Query')); - - expect(query).toBeDefined(); - }, 5000); -}); - -function cli(args: string[], cwd: string): Promise { - return new Promise(resolve => { - exec(`node ${path.resolve('./dist/cli.js')} ${args.join(' ')}`, - { cwd }, - (error, stdout, stderr) => { resolve({ - code: error && error.code ? error.code : 0, - error, - stdout, - stderr }); - }); - }); -} - -interface cliResult { - code: number, - error: any, - stdout: string, - stderr: string, -} \ No newline at end of file diff --git a/tests/helpers/__snapshots__/outputHelper.test.ts.snap b/tests/helpers/__snapshots__/outputHelper.test.ts.snap new file mode 100644 index 0000000..0aa5dcc --- /dev/null +++ b/tests/helpers/__snapshots__/outputHelper.test.ts.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GenerateOutput EXPECT standout output to be returned 1`] = ` +"Archived = false +Downvotes = 0 +Hidden = false +Permalink = /r/Rabbits/comments/1dj8pbt/this_is_my_ms_bear/ +Subreddit = Rabbits +Subreddit Subscribers = 654751 +Title = This is my Ms Bear! +Upvotes = 17 +Url = https://preview.redd.it/d5yno653zf7d1.jpg?width=640&crop=smart&auto=webp&s=5064d1caec3c12ac2855eb57ff131d0b313d5e9d" +`; + +exports[`GenerateOutput GIVEN options.json is true, EXPECT output to be returned as JSON 1`] = `"{"Archived":false,"Downs":0,"Hidden":false,"Permalink":"/r/Rabbits/comments/1dj8pbt/this_is_my_ms_bear/","Subreddit":"Rabbits","SubredditSubscribers":654751,"Title":"This is my Ms Bear!","Ups":17,"Url":"https://preview.redd.it/d5yno653zf7d1.jpg?width=640&crop=smart&auto=webp&s=5064d1caec3c12ac2855eb57ff131d0b313d5e9d"}"`; + +exports[`GenerateOutput GIVEN options.queryMetadata is supplied, EXPECT query metadata to be added 1`] = ` +"Archived = false +Downvotes = 0 +Hidden = false +Permalink = /r/Rabbits/comments/1dj8pbt/this_is_my_ms_bear/ +Subreddit = Rabbits +Subreddit Subscribers = 654751 +Title = This is my Ms Bear! +Upvotes = 17 +Url = https://preview.redd.it/d5yno653zf7d1.jpg?width=640&crop=smart&auto=webp&s=5064d1caec3c12ac2855eb57ff131d0b313d5e9d +Query.Subreddit = rabbits +Query.Sort By = hot" +`; diff --git a/tests/helpers/cliHelper.test.ts b/tests/helpers/cliHelper.test.ts new file mode 100644 index 0000000..3ea6687 --- /dev/null +++ b/tests/helpers/cliHelper.test.ts @@ -0,0 +1,118 @@ +import fs from "fs"; +import CliHelper from "../../src/helpers/cliHelper"; +import ICliOptions from "../../src/contracts/ICliOptions"; +import IReturnResult from "../../src/contracts/IReturnResult"; +import OutputHelper from "../../src/helpers/outputHelper"; +import { ErrorCode } from "../../src/constants/ErrorCode"; + +describe("Endpoint", () => { + describe("GIVEN response is successful", () => { + test("GIVEN options.o is defined, EXPECT output written to file", () => { + // Arrange + const response = { + IsSuccess: true, + } as IReturnResult; + + const options = { + o: "file.txt", + } as ICliOptions; + + OutputHelper.GenerateOutput = jest.fn().mockReturnValue("test output"); + + fs.writeFileSync = jest.fn(); + + console.log = jest.fn(); + + console.error = jest.fn(); + + // Act + const result = CliHelper.Endpoint(response, options); + + // Assert + expect(result).toBe(0); + + expect(OutputHelper.GenerateOutput).toHaveBeenCalledTimes(1); + expect(OutputHelper.GenerateOutput).toHaveBeenCalledWith(response, options); + + expect(fs.writeFileSync).toHaveBeenCalledTimes(1); + expect(fs.writeFileSync).toHaveBeenCalledWith("file.txt", "test output"); + + expect(console.log).not.toHaveBeenCalled(); + + expect(console.error).not.toHaveBeenCalled(); + }); + + test("GIVEN options.o is undefined, EXPECT output logged to console", () => { + // Arrange + const response = { + IsSuccess: true, + } as IReturnResult; + + const options = { + o: undefined, + } as ICliOptions; + + OutputHelper.GenerateOutput = jest.fn().mockReturnValue("test output"); + + fs.writeFileSync = jest.fn(); + + console.log = jest.fn(); + + console.error = jest.fn(); + + // Act + const result = CliHelper.Endpoint(response, options); + + // Assert + expect(result).toBe(0); + + expect(OutputHelper.GenerateOutput).toHaveBeenCalledTimes(1); + expect(OutputHelper.GenerateOutput).toHaveBeenCalledWith(response, options); + + expect(fs.writeFileSync).not.toHaveBeenCalled(); + + expect(console.log).toHaveBeenCalledTimes(1); + expect(console.log).toHaveBeenCalledWith("test output"); + + expect(console.error).not.toHaveBeenCalled(); + }); + }); + + test("GIVEN response is failure, EXPECT error logged to console", () => { + // Arrange + const response = { + IsSuccess: false, + Error: { + Message: "error message", + Code: ErrorCode.FailedToFetchReddit, + }, + } as IReturnResult; + + const options = { + o: "file.txt", + } as ICliOptions; + + OutputHelper.GenerateOutput = jest.fn().mockReturnValue("test output"); + + fs.writeFileSync = jest.fn(); + + console.log = jest.fn(); + + console.error = jest.fn(); + + // Act + const result = CliHelper.Endpoint(response, options); + + // Assert + expect(result).toBe(1); + + expect(OutputHelper.GenerateOutput).not.toHaveBeenCalled(); + + expect(fs.writeFileSync).not.toHaveBeenCalled(); + + expect(console.log).not.toHaveBeenCalled(); + + expect(console.error).toHaveBeenCalledTimes(1); + expect(console.error).toHaveBeenCalledWith("error message", ErrorCode.FailedToFetchReddit); + }); +}); \ No newline at end of file diff --git a/tests/helpers/outputHelper.test.ts b/tests/helpers/outputHelper.test.ts new file mode 100644 index 0000000..8c9893d --- /dev/null +++ b/tests/helpers/outputHelper.test.ts @@ -0,0 +1,99 @@ +import ICliOptions from "../../src/contracts/ICliOptions"; +import IReturnResult from "../../src/contracts/IReturnResult"; +import OutputHelper from "../../src/helpers/outputHelper"; + +describe("GenerateOutput", () => { + test("EXPECT standout output to be returned", () => { + // Arrange + const response = { + IsSuccess: true, + Query: { + subreddit: "rabbits", + sortBy: "hot", + }, + Result: { + Archived: false, + Downs: 0, + Hidden: false, + Permalink: "/r/Rabbits/comments/1dj8pbt/this_is_my_ms_bear/", + Subreddit: "Rabbits", + SubredditSubscribers: 654751, + Title: "This is my Ms Bear!", + Ups: 17, + Url: "https://preview.redd.it/d5yno653zf7d1.jpg?width=640&crop=smart&auto=webp&s=5064d1caec3c12ac2855eb57ff131d0b313d5e9d", + }, + } as IReturnResult; + + const options = {} as ICliOptions; + + // Act + const result = OutputHelper.GenerateOutput(response, options); + + // Assert + expect(result).toMatchSnapshot(); + }); + + test("GIVEN options.json is true, EXPECT output to be returned as JSON", () => { + // Arrange + const response = { + IsSuccess: true, + Query: { + subreddit: "rabbits", + sortBy: "hot", + }, + Result: { + Archived: false, + Downs: 0, + Hidden: false, + Permalink: "/r/Rabbits/comments/1dj8pbt/this_is_my_ms_bear/", + Subreddit: "Rabbits", + SubredditSubscribers: 654751, + Title: "This is my Ms Bear!", + Ups: 17, + Url: "https://preview.redd.it/d5yno653zf7d1.jpg?width=640&crop=smart&auto=webp&s=5064d1caec3c12ac2855eb57ff131d0b313d5e9d", + }, + } as IReturnResult; + + const options = { + json: true, + } as ICliOptions; + + // Act + const result = OutputHelper.GenerateOutput(response, options); + + // Assert + expect(result).toMatchSnapshot(); + }); + + test("GIVEN options.queryMetadata is supplied, EXPECT query metadata to be added", () => { + // Arrange + const response = { + IsSuccess: true, + Query: { + subreddit: "rabbits", + sortBy: "hot", + }, + Result: { + Archived: false, + Downs: 0, + Hidden: false, + Permalink: "/r/Rabbits/comments/1dj8pbt/this_is_my_ms_bear/", + Subreddit: "Rabbits", + SubredditSubscribers: 654751, + Title: "This is my Ms Bear!", + Ups: 17, + Url: "https://preview.redd.it/d5yno653zf7d1.jpg?width=640&crop=smart&auto=webp&s=5064d1caec3c12ac2855eb57ff131d0b313d5e9d", + }, + } as IReturnResult; + + const options = { + queryMetadata: true, + } as ICliOptions; + + // Act + const result = OutputHelper.GenerateOutput(response, options); + + // Assert + expect(result).toMatchSnapshot(); + }); +}); \ No newline at end of file