Compare commits

..

No commits in common. "master" and "v2.1.0" have entirely different histories.

21 changed files with 1987 additions and 3469 deletions

74
.drone.yml Normal file
View file

@ -0,0 +1,74 @@
---
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:
- 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

9
.gitignore vendored
View file

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

View file

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

View file

@ -1,92 +0,0 @@
steps:
build:
# Build and test
image: node
commands:
- yarn install --frozen-lockfile
- yarn build
when:
event: [ push, pull_request ]
branch: [ hotfix/*, feature/*, renovate/*, develop, master ]
lint:
image: node
commands:
- yarn lint
when:
event: [ push, pull_request ]
branch: [ hotfix/*, feature/*, renovate/*, develop, master ]
test:
image: node
commands:
- yarn test
when:
event: [ push, pull_request ]
branch: [ hotfix/*, feature/*, renovate/*, develop, master ]
# Package production build
package_prod:
image: node
commands:
- apt install unzip -y
- wget https://github.com/xerub/ldid/releases/download/42/ldid.zip
- unzip ldid.zip
- mkdir /opt/ldid/
- cp linux64/ldid /opt/ldid/
- export PATH=/opt/ldid:$PATH
- yarn package
when:
event: push
branch: master
rsync_prod:
image: alpine
secrets: [ ssh_key ]
commands:
- apk add rsync openssh-client
- eval `ssh-agent -s`
- echo "$SSH_KEY" | tr -d '\r' | ssh-add -
- rsync -e "ssh -o StrictHostKeyChecking=no" -r ./bin vylpes@192.168.1.115:/home/vylpes/apps/random-bunny/random-bunny_prod
when:
event: push
branch: master
# Package stage build
package_stage:
image: node
commands:
- apt install unzip -y
- wget https://github.com/xerub/ldid/releases/download/42/ldid.zip
- unzip ldid.zip
- mkdir /opt/ldid/
- cp linux64/ldid /opt/ldid/
- export PATH=/opt/ldid:$PATH
- yarn package
when:
event: push
branch: develop
rsync_stage:
image: alpine
secrets: [ ssh_key ]
commands:
- apk add rsync openssh-client
- eval `ssh-agent -s`
- echo "$SSH_KEY" | tr -d '\r' | ssh-add -
- rsync -e "ssh -o StrictHostKeyChecking=no" -r ./bin vylpes@192.168.1.115:/home/vylpes/apps/random-bunny/random-bunny_stage
when:
event: push
branch: develop
# Publish to NPM
publish:
image: plugins/npm
settings:
username:
from_secret: NPM_USERNAME
email:
from_secret: NPM_EMAIL
password:
from_secret: NPM_PASSWORD
token:
from_secret: NPM_TOKEN
when:
event: push
branch: master

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,97 +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 [Gitea Releases](https://gitea.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`.
> **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.
```
$ randombunny
Archived = false
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.
```
$ randombunny --help
# or
$ randombunny -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
--sort <sort> Sort by (choices: "hot", "new", "top", default: "hot")
-h, --help display help for command
✨ Done in 0.32s.
```
## JSON output
You can also convert the output into JSON, if you need to input it to another program.
```bash
$ randombunny --json
# or
$ randonbunny -j
{"Archived":false,"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".
```
$ randombunny --sort hot
$ randombunny --sort new
$ randomBunny --sort top
```
## Subreddit
You can change the subreddit which the command fetches from.
This defaults to "rabbits"
```
$ randombunny --subreddit rabbits
$ randombunny -s horses
```

View file

@ -1,13 +1,11 @@
{
"name": "random-bunny",
"version": "2.2.0",
"version": "2.1.0",
"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,19 +16,16 @@
"placeholder"
],
"dependencies": {
"commander": "^12.0.0",
"glob-parent": "^6.0.0",
"got-cjs": "^12.5.4",
"htmlparser2": "^9.1.0",
"linqts": "^1.14.4"
},
"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",
@ -40,35 +35,20 @@
"funding": "https://ko-fi.com/vylpes",
"devDependencies": {
"@types/eslint": "^8.21.1",
"@types/jest": "^29.5.8",
"@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@types/jest": "^29.4.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",
"eslint": "^8.35.0",
"jest": "^29.4.3",
"jest-mock-extended": "^3.0.3",
"np": "^9.0.0",
"pkg": "^5.8.1",
"ts-jest": "^29.1.1",
"np": "^7.7.0",
"ts-jest": "^29.0.5",
"ts-mockito": "^2.6.1",
"ts-node": "^10.9.1",
"typescript": "^5.0.0"
},
"resolutions": {
"np/**/got": "^14.0.0",
"**/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
@ -48,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,56 +0,0 @@
import { Command, Option } from "commander";
import randomBunny from "./index";
import ICliOptions from "./contracts/ICliOptions";
import { exit } from "process";
const program = new Command();
program
.name('random-bunny')
.description('Get a random image url from a subreddit of your choosing')
.version('2.2')
.option('-s, --subreddit <subreddit>', 'The subreddit to search', 'rabbits')
.option('-j, --json', 'Output as JSON')
.option('-q, --query-metadata', 'Include query metadata in result')
.addOption(new Option('--sort <sort>', 'Sort by').default('hot').choices(['hot', 'new', 'top']));
program.parse();
const options: ICliOptions = program.opts();
randomBunny(options.subreddit, options.sort)
.then((response) => {
if (response.IsSuccess) {
const result = response.Result!;
const outputLines: string[] = [];
if (options.json) {
console.log(JSON.stringify(result));
return;
}
outputLines.push(`Archived = ${result.Archived}`);
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}`);
}
console.log(outputLines.join("\n"));
exit(0);
} else {
const error = response.Error!;
console.error(error.Message, error.Code);
exit(1);
}
});

View file

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

View file

@ -1,5 +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";
}

View file

@ -1,6 +0,0 @@
export default interface ICliOptions {
subreddit: string,
json?: boolean,
sort: string,
queryMetadata?: boolean,
}

View file

@ -1,6 +0,0 @@
import { ErrorCode } from "../constants/ErrorCode";
export default interface IError {
Code: ErrorCode;
Message: 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,4 +0,0 @@
export default interface QueryResult {
subreddit: string,
sortBy: string,
}

View file

@ -1,19 +0,0 @@
import fetch from "got-cjs";
import * as htmlparser from "htmlparser2";
export default class ImageHelper {
public static async FetchImageFromRedditGallery(url: string): Promise<string | undefined> {
const fetched = await fetch(url);
if (!fetched || fetched.errored || fetched.statusCode != 200) {
return undefined;
}
const dom = htmlparser.parseDocument(fetched.body);
const img = htmlparser.DomUtils.findOne((x => x.tagName == "img" && x.attributes.find(y => y.value.includes("https://preview.redd.it")) != null), dom.children, true);
const imgSrc = img?.attributes.find(x => x.name == "src")?.value;
return imgSrc;
}
}

View file

@ -1,11 +1,8 @@
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 "./imageHelper";
import IFetchResult from "./contracts/IFetchResult.js";
const sortable = [
'new',
@ -13,28 +10,14 @@ const sortable = [
'top'
];
export default async function randomBunny(subreddit: string, sortBy: string = 'hot'): Promise<IReturnResult> {
if (!sortable.includes(sortBy)) sortBy = 'hot';
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`)
.then((res) => {
return res;
})
.catch(() => {
return null;
});
const result = await fetch(`https://reddit.com/r/${subreddit}/${sortBy}.json`);
if (!result) {
return {
IsSuccess: false,
Query: {
subreddit: subreddit,
sortBy: sortBy,
},
Error: {
Code: ErrorCode.FailedToFetchReddit,
Message: ErrorMessages.FailedToFetchReddit,
},
IsSuccess: false
}
}
@ -42,22 +25,14 @@ export default async function randomBunny(subreddit: string, sortBy: string = 'h
if (!json) {
return {
IsSuccess: false,
Query: {
subreddit: subreddit,
sortBy: sortBy,
},
Error: {
Code: ErrorCode.UnableToParseJSON,
Message: ErrorMessages.UnableToParseJSON,
},
IsSuccess: false
}
}
const data: IFetchResult[] = json.data.children;
const dataWithImages = new List<IFetchResult>(data)
.Where(x => x!.data.url.includes('.jpg') || x!.data.url.includes('.png') || x!.data.url.includes("/gallery/"))
.Where(x => x!.data.url.includes('.jpg') || x!.data.url.includes('.png'))
.ToArray();
let random = 0;
@ -65,47 +40,15 @@ export default async function randomBunny(subreddit: string, sortBy: string = 'h
if (dataWithImages.length == 0) {
return {
IsSuccess: false,
Query: {
subreddit: subreddit,
sortBy: sortBy,
},
Error: {
Code: ErrorCode.NoImageResultsFound,
Message: ErrorMessages.NoImageResultsFound,
},
};
} else {
random = Math.floor((Math.random() * (dataWithImages.length - 1)) + 0); // Between 0 and (size - 1)
} else if (dataWithImages.length > 1) {
random = Math.floor((Math.random() * dataWithImages.length - 1) + 0); // Between 0 and (size - 1)
}
const randomSelect = dataWithImages[random];
const randomData = randomSelect.data;
let url: string;
if (randomData.url.includes("/gallery")) {
const galleryImage = await ImageHelper.FetchImageFromRedditGallery(randomData.url);
if (!galleryImage) {
return {
IsSuccess: false,
Query: {
subreddit: subreddit,
sortBy: sortBy,
},
Error: {
Code: ErrorCode.NoImageResultsFound,
Message: ErrorMessages.NoImageResultsFound,
},
}
}
url = galleryImage;
} else {
url = randomData.url;
}
const redditResult: IRedditResult = {
Archived: randomData['archived'],
Downs: randomData['downs'],
@ -115,15 +58,11 @@ export default async function randomBunny(subreddit: string, sortBy: string = 'h
SubredditSubscribers: randomData['subreddit_subscribers'],
Title: randomData['title'],
Ups: randomData['ups'],
Url: url,
Url: randomData['url']
};
return {
IsSuccess: true,
Query: {
subreddit: subreddit,
sortBy: sortBy,
},
Result: redditResult
};
}

View file

@ -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 <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<cliResult> {
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,
}

View file

@ -1,90 +0,0 @@
import ImageHelper from "../src/imageHelper";
import fetch from "got-cjs";
jest.mock('got-cjs');
const fetchMock = jest.mocked(fetch);
describe("FetchImageFromRedditGallery", () => {
test("EXPECT image url to be returned", async () => {
fetchMock.mockResolvedValue({
body: "<html><body><img src='https://preview.redd.it/image.png' /></body></html>",
errored: undefined,
statusCode: 200,
});
const result = await ImageHelper.FetchImageFromRedditGallery("https://redd.it/gallery/image");
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith("https://redd.it/gallery/image");
expect(result).toBe("https://preview.redd.it/image.png");
});
test("GIVEN fetch is unable to return data, EXPECT undefined returned", async () => {
fetchMock.mockResolvedValue(null);
const result = await ImageHelper.FetchImageFromRedditGallery("https://redd.it/gallery/image");
expect(result).toBeUndefined();
});
test("GIVEN fetch is an error, EXPECT undefined returned", async () => {
fetchMock.mockResolvedValue({
body: "<html><body><img src='https://preview.redd.it/image.png' /></body></html>",
errored: "Error",
statusCode: 200,
});
const result = await ImageHelper.FetchImageFromRedditGallery("https://redd.it/gallery/image");
expect(result).toBeUndefined();
});
test("GIVEN fetch is not status code of 200, EXPECT undefined returned", async () => {
fetchMock.mockResolvedValue({
body: "<html><body><img src='https://preview.redd.it/image.png' /></body></html>",
errored: undefined,
statusCode: 500,
});
const result = await ImageHelper.FetchImageFromRedditGallery("https://redd.it/gallery/image");
expect(result).toBeUndefined();
});
test("GIVEN image tag is not found, EXPECT undefined returned", async () => {
fetchMock.mockResolvedValue({
body: "<html><body></body></html>",
errored: undefined,
statusCode: 200,
});
const result = await ImageHelper.FetchImageFromRedditGallery("https://redd.it/gallery/image");
expect(result).toBeUndefined();
});
test("GIVEN image source attribute is not found, EXPECT undefined returned", async () => {
fetchMock.mockResolvedValue({
body: "<html><body><img /></body></html>",
errored: undefined,
statusCode: 200,
});
const result = await ImageHelper.FetchImageFromRedditGallery("https://redd.it/gallery/image");
expect(result).toBeUndefined();
});
test("GIVEN image source attribute is not found that is a preview.redd.it url, EXPECT undefined returned", async () => {
fetchMock.mockResolvedValue({
body: "<html><body><img src='main.png' /></body></html>",
errored: undefined,
statusCode: 200,
});
const result = await ImageHelper.FetchImageFromRedditGallery("https://redd.it/gallery/image");
expect(result).toBeUndefined();
});
});

View file

@ -1,6 +1,3 @@
import { ErrorCode } from "../src/constants/ErrorCode";
import ErrorMessages from "../src/constants/ErrorMessages";
import ImageHelper from "../src/imageHelper";
import randomBunny from "../src/index";
import fetch from "got-cjs";
@ -35,9 +32,8 @@ describe('randomBunny', () => {
expect(result.IsSuccess).toBeTruthy();
expect(result.Result).toBeDefined();
expect(result.Error).toBeUndefined();
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json');
});
test('GIVEN sortBy is NOT supplied, expect it to default to hot', async () => {
@ -67,9 +63,8 @@ describe('randomBunny', () => {
expect(result.IsSuccess).toBeTruthy();
expect(result.Result).toBeDefined();
expect(result.Error).toBeUndefined();
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/hot.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/hot.json');
});
test('GIVEN sortBy is NOT valid, expect it to default to hot', async () => {
@ -99,24 +94,19 @@ describe('randomBunny', () => {
expect(result.IsSuccess).toBeTruthy();
expect(result.Result).toBeDefined();
expect(result.Error).toBeUndefined();
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/hot.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/hot.json');
});
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).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json');
});
test('GIVEN the result is NOT valid JSON, EXPECT failure result', async () => {
@ -128,12 +118,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).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json');
});
test('GIVEN randomSelect does NOT find a response, EXPECT failure result', async () => {
@ -149,12 +135,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).toBeCalledWith('https://reddit.com/r/rabbits/new.json?limit=100');
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json');
});
test('GIVEN randomSelect does NOT find a valid response, EXPECT failure result', async () => {
@ -184,83 +166,7 @@ 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).toBeCalledWith('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 () => {
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/gallery/cr8xudsnkgua1',
},
},
],
}
}),
});
ImageHelper.FetchImageFromRedditGallery = jest.fn().mockResolvedValue("https://i.redd.it/cr8xudsnkgua1.jpg")
const result = await randomBunny('rabbits', 'new');
expect(result.IsSuccess).toBeTruthy();
expect(result.Result).toBeDefined();
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");
});
test("GIVEN data fetched is a gallery AND an image is not returned from the helper, EXPECT error", 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/gallery/cr8xudsnkgua1',
},
},
],
}
}),
});
ImageHelper.FetchImageFromRedditGallery = jest.fn().mockResolvedValue(undefined)
const result = await randomBunny('rabbits', 'new');
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);
expect(fetchMock).toBeCalledWith('https://reddit.com/r/rabbits/new.json');
});
});

4539
yarn.lock

File diff suppressed because it is too large Load diff