Compare commits

..

No commits in common. "master" and "hotfix/2.1.6" have entirely different histories.

33 changed files with 1656 additions and 2882 deletions

75
.drone.yml Normal file
View file

@ -0,0 +1,75 @@
---
kind: pipeline
name: deployment
steps:
- name: build
image: node
commands:
- yarn install --frozen-lockfile
- yarn build
- name: test
image: node
commands:
- yarn test
depends_on:
- build
- name: lint
image: node
commands:
- yarn lint
depends_on:
- build
- name: deploy
image: plugins/npm
settings:
username: vylpes
password:
from_secret: npm_password
email: ethan@vylpes.com
token:
from_secret: npm_token
depends_on:
- test
- lint
trigger:
branch:
- master
event:
- push
- tag
---
kind: pipeline
name: integration
steps:
- name: build
image: node
commands:
- yarn install --frozen-lockfile
- yarn build
- name: test
image: node
commands:
- yarn test
depends_on:
- build
- name: lint
image: node
commands:
- yarn lint
depends_on:
- build
trigger:
branch:
- develop
- feature/*
- hotfix/*
- renovate/*
event:
- push

1
.eslintignore Normal file
View file

@ -0,0 +1 @@
dist/

50
.eslintrc Normal file
View file

@ -0,0 +1,50 @@
{
"parserOptions": {
"ecmaVersion": 6
},
"extends": [
"eslint:recommended"
],
"rules": {
"camelcase": "error",
"brace-style": [
"error",
"1tbs"
],
"comma-dangle": [
"error",
"never"
],
"comma-spacing": [
"error",
{
"before": false,
"after": true
}
],
"comma-style": [
"error",
"last"
],
"arrow-body-style": [
"error",
"as-needed"
],
"arrow-parens": [
"error",
"as-needed"
],
"arrow-spacing": "error",
"no-var": "error",
"prefer-template": "error",
"prefer-const": "error"
},
"globals": {
"exports": "writable",
"module": "writable",
"require": "writable",
"process": "writable",
"console": "writable",
"jest": "writable"
}
}

View file

@ -1,38 +0,0 @@
name: Publish to NPM
on:
push:
tags:
- '*'
jobs:
build:
runs-on: node
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn test
- run: yarn lint
publish:
needs: build
runs-on: node
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn package
- run: cp -r ./bin ${{ secrets.PROD_REPO_PATH }}
- uses: https://github.com/JS-DevTools/npm-publish@v3
with:
token: ${{ secrets.NPM_TOKEN }}

View file

@ -1,35 +0,0 @@
name: Stage
on:
push:
branches:
- develop
jobs:
build:
runs-on: node
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn test
- run: yarn lint
rsync:
needs: build
runs-on: node
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn package
- run: cp -r ./bin ${{ secrets.STAGE_REPO_PATH }}

View file

@ -1,25 +0,0 @@
name: Test
on:
push:
branches:
- feature/*
- hotfix/*
- renovate/*
- dependabot/*
jobs:
build:
runs-on: node
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn test
- run: yarn lint

2
.gitignore vendored
View file

@ -1,6 +1,4 @@
node_modules/
dist/
bin/
coverage/
yarn-error.log
.DS_Store

View file

@ -1,5 +1,5 @@
src/
bin/
app.ts
tests/
jest.config.cjs
jest.setup.js

9
app.ts Normal file
View file

@ -0,0 +1,9 @@
import randomBunny from "./dist/index.js";
async function app() {
const result = await randomBunny('rabbits', 'hot');
console.log(result);
}
app();

View file

@ -1,123 +0,0 @@
# CLI
Since Version 2.2, Random Bunny contains a command line interface (CLI).
## Downloads
The project can be downloaded as a binary for your system via the [GitHub Releases](https://github.com/Vylpes/random-bunny/releases) or [Forgejo Releases](https://git.vylpes.xyz/RabbitLabs/random-bunny/releases) page.
We currently support:
- Linux (x64)
- Windows (x64)
- macOS (x64, Arm64\*)
The git repository can also be cloned and ran via `yarn build` and `yarn start`.
You can produce the binary using the `yarn package` command. This creates the binaries in the `./bin` folder.
> **NOTE:** We are aware of a bug in the macOS Arm64 builds failing to execute. For now you're still able to use the x64 builds under Rosetta fine. This will hopefully be fixed in a future release.
## Default Output
By default, the command will fetch a random image from `r/rabbits` and return it in a human-readable output.
```
$ random-bunny
Archived = false
Author = Rabbit_Owner
Downvotes = 0
Hidden = false
Permalink = /r/Rabbits/comments/1av1rg9/cute_baby_bun/
Subreddit = Rabbits
Subreddit Subscribers = 486084
Title = Cute baby bun
Upvotes = 211
Url = https://i.redd.it/sfz0srdrimjc1.png
```
## Help
The command also includes a help option in case you are stuck.
```
$ random-bunny --help
# or
$ random-bunny -h
Usage: random-bunny [options]
Get a random image url from a subreddit of your choosing
Options:
-V, --version output the version number
-s, --subreddit <subreddit> The subreddit to search (default: "rabbits")
-j, --json Output as JSON
-q, --query-metadata Include query metadata in result
-o <file> Output to file
--sort <sort> Sort by (choices: "hot", "new", "top", default: "hot")
--limit <limit> The amount of posts to fetch from the reddit api (default: 100)
-h, --help display help for command
```
## JSON output
You can also convert the output into JSON, if you need to input it to another program.
```bash
$ random-bunny --json
# or
$ randon-bunny -j
{"Archived":false,"Author":"Rabbit_Owner","Downs":0,"Hidden":false,"Permalink":"/r/Rabbits/comments/1av1rg9/cute_baby_bun/","Subreddit":"Rabbits","SubredditSubscribers":486085,"Title":"Cute baby bun","Ups":210,"Url":"https://i.redd.it/sfz0srdrimjc1.png"}
```
## Sort
You can also choose the sorting option which reddit will use to return the available posts to randomise from.
This defaults to "hot". The valid options are "hot", "new", and "top".
```
$ random-bunny --sort hot
$ random-bunny --sort new
$ random-bunny --sort top
```
## Subreddit
You can change the subreddit which the command fetches from.
This defaults to "rabbits"
```
$ random-bunny --subreddit rabbits
$ random-bunny -s horses
```
## Output to file
If you'd rather send the output to a file, you can supply the `-o` flag.
```
$ randombunny -o ~/Desktop/output.txt
```
## Reddit API Return Limits
You can also limit the amount the posts the script requests from the Reddit API
using the `--limit` option.
This defaults to 100. This accepts any number between 1 and 100.
Please note limiting the calls to less than 100 will give a higher chance of
the script not finding any valid image post to return.
```
$ random-bunny --limit 50
```

View file

@ -1,54 +0,0 @@
import js from "@eslint/js";
import ts from "typescript-eslint";
export default [
{
ignores: [
"**/dist/",
"eslint.config.mjs",
"jest.config.cjs",
"jest.setup.js"
],
},
js.configs.recommended,
...ts.configs.recommended,
{
languageOptions: {
globals: {
exports: "writable",
module: "writable",
require: "writable",
process: "writable",
console: "writable",
jest: "writable",
},
ecmaVersion: 6,
sourceType: "script",
},
files: [
"./src",
"./tests"
],
rules: {
camelcase: "error",
"brace-style": ["error", "1tbs"],
"comma-dangle": ["error", "never"],
"comma-spacing": ["error", {
before: false,
after: true,
}],
"comma-style": ["error", "last"],
"arrow-body-style": ["error", "as-needed"],
"arrow-parens": ["error", "as-needed"],
"arrow-spacing": "error",
"no-var": "error",
"prefer-template": "error",
"prefer-const": "error",
},
}
];

View file

@ -1,13 +1,11 @@
{
"name": "random-bunny",
"version": "2.3.0",
"version": "2.1.5",
"description": "Get a random subreddit image url",
"license": "MIT",
"author": "Vylpes",
"exports": "./dist/index.js",
"typings": "./dist/index.d.ts",
"bin": "./dist/cli.js",
"main": "./dist/index.js",
"keywords": [
"rabbit",
"bunny",
@ -18,7 +16,6 @@
"placeholder"
],
"dependencies": {
"commander": "^12.0.0",
"glob-parent": "^6.0.0",
"got-cjs": "^12.5.4",
"htmlparser2": "^9.1.0",
@ -26,11 +23,10 @@
},
"scripts": {
"build": "tsc",
"start": "node dist/cli.js",
"start": "ts-node app.ts",
"test": "jest",
"lint": "eslint .",
"release": "np --no-publish",
"package": "pkg . --no-bytecode"
"release": "np --no-publish"
},
"bugs": {
"url": "https://gitea.vylpes.xyz/RabbitLabs/random-bunny/issues",
@ -39,39 +35,26 @@
"homepage": "https://gitea.vylpes.xyz/RabbitLabs/random-bunny",
"funding": "https://ko-fi.com/vylpes",
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.8.0",
"@types/eslint": "^9.6.0",
"@types/eslint": "^8.21.1",
"@types/jest": "^29.5.8",
"@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"@yao-pkg/pkg": "^5.12.0",
"eslint": "^9.8.0",
"@types/node": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"eslint": "^8.49.0",
"jest": "^29.7.0",
"jest-mock-extended": "^3.0.3",
"np": "^10.0.0",
"np": "^7.7.0",
"ts-jest": "^29.1.1",
"ts-mockito": "^2.6.1",
"typescript": "^5.0.0",
"typescript-eslint": "^7.18.0"
"ts-node": "^10.9.1",
"typescript": "^5.0.0"
},
"resolutions": {
"np/**/got": "^14.0.0",
"np/**/got": "^11.8.5",
"**/semver": "^7.5.2",
"@babel/traverse": "^7.23.2"
},
"files": [
"dist"
],
"pkg": {
"scripts": "dist/**/*.js",
"targets": [
"latest-linux-x64",
"latest-macos-x64",
"latest-macos-arm64",
"latest-win-x64"
],
"outputPath": "bin"
}
]
}

View file

@ -14,10 +14,6 @@ or if you're using yarn
yarn add random-bunny
```
### Binary
The project can be downloaded as a binary for your system via the [GitHub Releases](https://github.com/Vylpes/random-bunny/releases) or [Gitea Releases](https://gitea.vylpes.xyz/RabbitLabs/random-bunny/releases) page.
## Usage
@ -33,11 +29,10 @@ console.log(result);
### `randomBunny()`
Returns a `json string` for a random post. Accepts 3 arguments: `subreddit`, `sortby` ('new', 'hot', 'top'), and `limit` (1-100, default 100)
Returns a `json string` for a random post. Accepts 2 arguments: `subreddit`, and `sortby` ('new', 'hot', 'top')
The json string which gets returned consists of:
- archived
- author
- downs
- hidden
- permalink
@ -49,12 +44,6 @@ The json string which gets returned consists of:
`sortBy` will default to 'hot' if not given or invalid
## CLI
Random bunny can also be used as a CLI. This is accessible via the executable (see git releases) or via `src/cli.ts`
For more details, see the [documentation](https://docs.vylpes.xyz/books/random-bunny).
## Notes
* Node 4 or newer.

View file

@ -1,5 +1,4 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"baseBranches": ["develop"],
"labels": ["type/dependencies"]
"baseBranches": ["develop"]
}

View file

@ -1,25 +0,0 @@
import { Command, Option } from "commander";
import randomBunny from "./index";
import ICliOptions from "./contracts/ICliOptions";
import { exit } from "process";
import CliHelper from "./helpers/cliHelper";
const program = new Command();
program
.name('random-bunny')
.description('Get a random image url from a subreddit of your choosing')
.version('2.3')
.option('-s, --subreddit <subreddit>', 'The subreddit to search', 'rabbits')
.option('-j, --json', 'Output as JSON')
.option('-q, --query-metadata', 'Include query metadata in result')
.option('-o <file>', 'Output to file')
.addOption(new Option('--sort <sort>', 'Sort by').default('hot').choices(['hot', 'new', 'top']))
.addOption(new Option('--limit <limit>', 'The amount of posts to fetch from the reddit api').default(100));
program.parse();
const options: ICliOptions = program.opts();
randomBunny(options.subreddit, options.sort, options.limit)
.then((response) => exit(CliHelper.Endpoint(response, options)));

View file

@ -1,7 +0,0 @@
export enum ErrorCode {
Unknown,
FailedToFetchReddit,
UnableToParseJSON,
NoImageResultsFound,
LimitOutOfRange,
}

View file

@ -1,6 +0,0 @@
export default class ErrorMessages {
public static readonly FailedToFetchReddit = "Failed to fetch result from Reddit";
public static readonly UnableToParseJSON = "Unable to parse the JSON result";
public static readonly NoImageResultsFound = "No image results found in response from Reddit";
public static readonly LimitOutOfRange = "Limit must be a number between 1 and 100";
}

View file

@ -1,8 +0,0 @@
export default interface ICliOptions {
subreddit: string,
json?: boolean,
sort: "new" | "hot" | "top",
o?: string,
limit: number,
queryMetadata?: boolean,
}

View file

@ -1,6 +0,0 @@
import { ErrorCode } from "../constants/ErrorCode";
export default interface IError {
Code: ErrorCode;
Message: string;
}

View file

@ -1,7 +1,6 @@
export default interface IFetchResult {
data: {
archived: boolean,
author: string,
downs: number,
hidden: boolean,
permalink: string,

View file

@ -1,6 +1,5 @@
export default interface IRedditResult {
Archived: boolean,
Author: string,
Downs: number,
Hidden: boolean,
Permalink: string,

View file

@ -1,10 +1,6 @@
import IError from "./IError.js";
import IRedditResult from "./IRedditResult.js";
import QueryResult from "./QueryResult.js";
export default interface IReturnResult {
IsSuccess: boolean;
Query: QueryResult;
Result?: IRedditResult;
Error?: IError;
}

View file

@ -1,5 +0,0 @@
export default interface QueryResult {
subreddit: string,
sortBy: string,
limit: number,
}

View file

@ -1,25 +0,0 @@
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}\n`);
} else {
console.log(output);
}
return 0;
} else {
const error = response.Error!;
console.error(error.Message, error.Code);
return 1;
}
}
}

View file

@ -1,33 +0,0 @@
import ICliOptions from "../contracts/ICliOptions";
import IReturnResult from "../contracts/IReturnResult";
export default class OutputHelper {
public static GenerateOutput(response: IReturnResult, options: ICliOptions): string {
const result = response.Result!;
const outputLines: string[] = [];
if (options.json) {
return JSON.stringify(result);
}
outputLines.push(`Archived = ${result.Archived}`);
outputLines.push(`Author = ${result.Author}`);
outputLines.push(`Downvotes = ${result.Downs}`);
outputLines.push(`Hidden = ${result.Hidden}`);
outputLines.push(`Permalink = ${result.Permalink}`);
outputLines.push(`Subreddit = ${result.Subreddit}`);
outputLines.push(`Subreddit Subscribers = ${result.SubredditSubscribers}`);
outputLines.push(`Title = ${result.Title}`);
outputLines.push(`Upvotes = ${result.Ups}`);
outputLines.push(`Url = ${result.Url}`);
if (options.queryMetadata != null) {
outputLines.push(`Query.Subreddit = ${response.Query.subreddit}`);
outputLines.push(`Query.Sort By = ${response.Query.sortBy}`);
outputLines.push(`Query.Limit = ${response.Query.limit}`);
}
return outputLines.join("\n");
}
}

View file

@ -1,48 +1,24 @@
import IReturnResult from "./contracts/IReturnResult";
import IRedditResult from "./contracts/IRedditResult";
import IReturnResult from "./contracts/IReturnResult.js";
import IRedditResult from "./contracts/IRedditResult.js";
import fetch from "got-cjs";
import { List } from 'linqts';
import IFetchResult from "./contracts/IFetchResult";
import { ErrorCode } from "./constants/ErrorCode";
import ErrorMessages from "./constants/ErrorMessages";
import ImageHelper from "./helpers/imageHelper";
import IFetchResult from "./contracts/IFetchResult.js";
import ImageHelper from "./imageHelper";
export default async function randomBunny(subreddit: string, sortBy: "new" | "hot" | "top" = 'hot', limit: number = 100): Promise<IReturnResult> {
if (limit < 1 || limit > 100) {
return {
IsSuccess: false,
Query: {
subreddit: subreddit,
sortBy: sortBy,
limit: limit,
},
Error: {
Code: ErrorCode.LimitOutOfRange,
Message: ErrorMessages.LimitOutOfRange,
}
};
}
const sortable = [
'new',
'hot',
'top'
];
const result = await fetch(`https://reddit.com/r/${subreddit}/${sortBy}.json?limit=${limit}`)
.then((res) => {
return res;
})
.catch(() => {
return null;
});
export default async function randomBunny(subreddit: string, sortBy?: string): Promise<IReturnResult> {
if (!sortBy || !sortable.includes(sortBy)) sortBy = 'hot';
const result = await fetch(`https://reddit.com/r/${subreddit}/${sortBy}.json?limit=100`);
if (!result) {
return {
IsSuccess: false,
Query: {
subreddit: subreddit,
sortBy: sortBy,
limit: limit,
},
Error: {
Code: ErrorCode.FailedToFetchReddit,
Message: ErrorMessages.FailedToFetchReddit,
},
IsSuccess: false
}
}
@ -50,16 +26,7 @@ export default async function randomBunny(subreddit: string, sortBy: "new" | "ho
if (!json) {
return {
IsSuccess: false,
Query: {
subreddit: subreddit,
sortBy: sortBy,
limit: limit,
},
Error: {
Code: ErrorCode.UnableToParseJSON,
Message: ErrorMessages.UnableToParseJSON,
},
IsSuccess: false
}
}
@ -74,15 +41,6 @@ export default async function randomBunny(subreddit: string, sortBy: "new" | "ho
if (dataWithImages.length == 0) {
return {
IsSuccess: false,
Query: {
subreddit: subreddit,
sortBy: sortBy,
limit: limit,
},
Error: {
Code: ErrorCode.NoImageResultsFound,
Message: ErrorMessages.NoImageResultsFound,
},
};
} else {
random = Math.floor((Math.random() * (dataWithImages.length - 1)) + 0); // Between 0 and (size - 1)
@ -100,15 +58,6 @@ export default async function randomBunny(subreddit: string, sortBy: "new" | "ho
if (!galleryImage) {
return {
IsSuccess: false,
Query: {
subreddit: subreddit,
sortBy: sortBy,
limit: limit,
},
Error: {
Code: ErrorCode.NoImageResultsFound,
Message: ErrorMessages.NoImageResultsFound,
},
}
}
@ -118,7 +67,6 @@ export default async function randomBunny(subreddit: string, sortBy: "new" | "ho
}
const redditResult: IRedditResult = {
Author: randomData['author'],
Archived: randomData['archived'],
Downs: randomData['downs'],
Hidden: randomData['hidden'],
@ -132,11 +80,6 @@ export default async function randomBunny(subreddit: string, sortBy: "new" | "ho
return {
IsSuccess: true,
Query: {
subreddit: subreddit,
sortBy: sortBy,
limit: limit,
},
Result: redditResult
};
}

View file

@ -1,32 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GenerateOutput EXPECT standout output to be returned 1`] = `
"Archived = false
Author = author
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,"Author":"author","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
Author = author
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
Query.Limit = 100"
`;

View file

@ -1,118 +0,0 @@
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\n");
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);
});
});

View file

@ -1,105 +0,0 @@
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",
limit: 100,
},
Result: {
Archived: false,
Author: 'author',
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",
limit: 100,
},
Result: {
Archived: false,
Author: 'author',
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",
limit: 100,
},
Result: {
Archived: false,
Author: 'author',
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();
});
});

View file

@ -1,4 +1,4 @@
import ImageHelper from "../../src/helpers/imageHelper";
import ImageHelper from "../src/imageHelper";
import fetch from "got-cjs";
jest.mock('got-cjs');

View file

@ -1,16 +1,10 @@
import { ErrorCode } from "../src/constants/ErrorCode";
import ErrorMessages from "../src/constants/ErrorMessages";
import ImageHelper from "../src/helpers/imageHelper";
import ImageHelper from "../src/imageHelper";
import randomBunny from "../src/index";
import fetch from "got-cjs";
jest.mock('got-cjs');
const fetchMock = jest.mocked(fetch);
beforeEach(() => {
fetchMock.mockReset();
});
describe('randomBunny', () => {
test('GIVEN subreddit AND sortBy is supplied, EXPECT successful result', async() => {
fetchMock.mockResolvedValue({
@ -20,7 +14,6 @@ describe('randomBunny', () => {
{
data: {
archived: false,
author: 'author',
downs: 0,
hidden: false,
permalink: '/r/Rabbits/comments/12pa5te/someone_told_pickles_its_monday_internal_fury/',
@ -40,9 +33,8 @@ describe('randomBunny', () => {
expect(result.IsSuccess).toBeTruthy();
expect(result.Result).toBeDefined();
expect(result.Error).toBeUndefined();
expect(fetchMock).toHaveBeenCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
});
test('GIVEN sortBy is NOT supplied, expect it to default to hot', async () => {
@ -53,7 +45,6 @@ describe('randomBunny', () => {
{
data: {
archived: false,
author: 'author',
downs: 0,
hidden: false,
permalink: '/r/Rabbits/comments/12pa5te/someone_told_pickles_its_monday_internal_fury/',
@ -73,24 +64,50 @@ describe('randomBunny', () => {
expect(result.IsSuccess).toBeTruthy();
expect(result.Result).toBeDefined();
expect(result.Error).toBeUndefined();
expect(fetchMock).toHaveBeenCalledWith('https://reddit.com/r/rabbits/hot.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/hot.json?limit=100');
});
test('GIVEN sortBy is NOT valid, expect it to default to hot', async () => {
fetchMock.mockResolvedValue({
body: JSON.stringify({
data: {
children: [
{
data: {
archived: false,
downs: 0,
hidden: false,
permalink: '/r/Rabbits/comments/12pa5te/someone_told_pickles_its_monday_internal_fury/',
subreddit: 'Rabbits',
subreddit_subscribers: 298713,
title: 'Someone told pickles its Monday… *internal fury*',
ups: 1208,
url: 'https://i.redd.it/cr8xudsnkgua1.jpg',
},
},
],
}
}),
});
const result = await randomBunny('rabbits', 'invalid');
expect(result.IsSuccess).toBeTruthy();
expect(result.Result).toBeDefined();
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/hot.json?limit=100');
});
test('GIVEN the fetch fails, EXPECT failure result', async () => {
fetchMock.mockRejectedValue('Test Reason')
fetchMock.mockResolvedValue(null);
const result = await randomBunny('rabbits', 'new');
expect(result.IsSuccess).toBeFalsy();
expect(result.Result).toBeUndefined();
expect(result.Error).toBeDefined();
expect(result.Error!.Code).toBe(ErrorCode.FailedToFetchReddit);
expect(result.Error!.Message).toBe(ErrorMessages.FailedToFetchReddit);
expect(fetchMock).toHaveBeenCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
});
test('GIVEN the result is NOT valid JSON, EXPECT failure result', async () => {
@ -102,12 +119,8 @@ describe('randomBunny', () => {
expect(result.IsSuccess).toBeFalsy();
expect(result.Result).toBeUndefined();
expect(result.Error).toBeDefined();
expect(result.Error!.Code).toBe(ErrorCode.UnableToParseJSON);
expect(result.Error!.Message).toBe(ErrorMessages.UnableToParseJSON);
expect(fetchMock).toHaveBeenCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
});
test('GIVEN randomSelect does NOT find a response, EXPECT failure result', async () => {
@ -123,12 +136,8 @@ describe('randomBunny', () => {
expect(result.IsSuccess).toBeFalsy();
expect(result.Result).toBeUndefined();
expect(result.Error).toBeDefined();
expect(result.Error!.Code).toBe(ErrorCode.NoImageResultsFound);
expect(result.Error!.Message).toBe(ErrorMessages.NoImageResultsFound);
expect(fetchMock).toHaveBeenCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
});
test('GIVEN randomSelect does NOT find a valid response, EXPECT failure result', async () => {
@ -139,7 +148,6 @@ describe('randomBunny', () => {
{
data: {
archived: false,
author: 'author',
downs: 0,
hidden: false,
permalink: '/r/Rabbits/comments/12pa5te/someone_told_pickles_its_monday_internal_fury/',
@ -159,13 +167,8 @@ describe('randomBunny', () => {
expect(result.IsSuccess).toBeFalsy();
expect(result.Result).toBeUndefined();
expect(result.Error).toBeDefined();
expect(result.Error!.Code).toBe(ErrorCode.NoImageResultsFound);
expect(result.Error!.Message).toBe(ErrorMessages.NoImageResultsFound);
expect(fetchMock).toHaveBeenCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toHaveBeenCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
});
test("GIVEN data fetched is a gallery AND an image is returned from the helper, EXPECT this to be used", async () => {
@ -176,7 +179,6 @@ describe('randomBunny', () => {
{
data: {
archived: false,
author: 'author',
downs: 0,
hidden: false,
permalink: '/r/Rabbits/comments/12pa5te/someone_told_pickles_its_monday_internal_fury/',
@ -199,10 +201,10 @@ describe('randomBunny', () => {
expect(result.IsSuccess).toBeTruthy();
expect(result.Result).toBeDefined();
expect(fetchMock).toHaveBeenCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(ImageHelper.FetchImageFromRedditGallery).toHaveBeenCalledTimes(1);
expect(ImageHelper.FetchImageFromRedditGallery).toHaveBeenCalledWith("https://i.redd.it/gallery/cr8xudsnkgua1");
expect(ImageHelper.FetchImageFromRedditGallery).toHaveBeenCalledWith("https://i.redd.it/gallery/cr8xudsnkgua1")
});
test("GIVEN data fetched is a gallery AND an image is not returned from the helper, EXPECT error", async () => {
@ -213,7 +215,6 @@ describe('randomBunny', () => {
{
data: {
archived: false,
author: 'author',
downs: 0,
hidden: false,
permalink: '/r/Rabbits/comments/12pa5te/someone_told_pickles_its_monday_internal_fury/',
@ -236,113 +237,5 @@ describe('randomBunny', () => {
expect(ImageHelper.FetchImageFromRedditGallery).toHaveBeenCalledTimes(1);
expect(result.IsSuccess).toBe(false);
expect(result.Error).toBeDefined();
expect(result.Error?.Code).toBe(ErrorCode.NoImageResultsFound);
expect(result.Error?.Message).toBe(ErrorMessages.NoImageResultsFound);
});
test("GIVEN limit is supplied, EXPECT limit sent to the API", async () => {
fetchMock.mockResolvedValue({
body: JSON.stringify({
data: {
children: [
{
data: {
archived: false,
author: 'author',
downs: 0,
hidden: false,
permalink: '/r/Rabbits/comments/12pa5te/someone_told_pickles_its_monday_internal_fury/',
subreddit: 'Rabbits',
subreddit_subscribers: 298713,
title: 'Someone told pickles its Monday… *internal fury*',
ups: 1208,
url: 'https://i.redd.it/cr8xudsnkgua1.jpg',
},
},
],
}
}),
});
const result = await randomBunny('rabbits', 'new', 50);
expect(result.IsSuccess).toBeTruthy();
expect(result.Result).toBeDefined();
expect(result.Error).toBeUndefined();
expect(fetchMock).toHaveBeenCalledWith('https://reddit.com/r/rabbits/new.json?limit=50');
});
test("GIVEN limit is less than 1, EXPECT error to be returned", async () => {
fetchMock.mockResolvedValue({
body: JSON.stringify({
data: {
children: [
{
data: {
archived: false,
author: 'author',
downs: 0,
hidden: false,
permalink: '/r/Rabbits/comments/12pa5te/someone_told_pickles_its_monday_internal_fury/',
subreddit: 'Rabbits',
subreddit_subscribers: 298713,
title: 'Someone told pickles its Monday… *internal fury*',
ups: 1208,
url: 'https://i.redd.it/cr8xudsnkgua1.jpg',
},
},
],
}
}),
});
const result = await randomBunny('rabbits', 'new', 0);
expect(result.IsSuccess).toBeFalsy();
expect(result.Result).toBeUndefined();
expect(result.Error).toBeDefined();
expect(result.Error!.Code).toBe(ErrorCode.LimitOutOfRange);
expect(result.Error!.Message).toBe(ErrorMessages.LimitOutOfRange);
expect(fetchMock).not.toHaveBeenCalled();
});
test("GIVEN limit is greater than 100, EXPECT error to be returned", async () => {
fetchMock.mockResolvedValue({
body: JSON.stringify({
data: {
children: [
{
data: {
archived: false,
author: 'author',
downs: 0,
hidden: false,
permalink: '/r/Rabbits/comments/12pa5te/someone_told_pickles_its_monday_internal_fury/',
subreddit: 'Rabbits',
subreddit_subscribers: 298713,
title: 'Someone told pickles its Monday… *internal fury*',
ups: 1208,
url: 'https://i.redd.it/cr8xudsnkgua1.jpg',
},
},
],
}
}),
});
const result = await randomBunny('rabbits', 'new', 101);
expect(result.IsSuccess).toBeFalsy();
expect(result.Result).toBeUndefined();
expect(result.Error).toBeDefined();
expect(result.Error!.Code).toBe(ErrorCode.LimitOutOfRange);
expect(result.Error!.Message).toBe(ErrorMessages.LimitOutOfRange);
expect(fetchMock).not.toHaveBeenCalled();
});
});

3409
yarn.lock

File diff suppressed because it is too large Load diff