nekosia.js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/examples/basicUsage.js +12 -0
- package/examples/dynamicCategoryFetch.js +15 -0
- package/examples/fetchShadowImages.js +6 -0
- package/examples/version.js +6 -0
- package/index.js +78 -0
- package/package.json +59 -0
- package/services/https.js +45 -0
- package/test/api.test.js +110 -0
- package/test/integration.test.js +92 -0
- package/types/index.d.ts +363 -0
- package/types/tags.ts +839 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Nekosia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>๐ผ๏ธ Nekosia.js API โ Random Anime Images</h1>
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
Nekosia.js is a Node.js module that provides easy access to the Nekosia API, a rich source of anime-themed images.
|
|
6
|
+
The API offers a wide range of categories, allowing you to quickly and flexibly search for images according to your preferences and needs.
|
|
7
|
+
You decide what images you want to retrieve, which makes our API stand out from others.
|
|
8
|
+
|
|
9
|
+
But that's not all! The API also supports sessions (based on user ID or IP address), helping to avoid repeated images.
|
|
10
|
+
|
|
11
|
+
<div align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/nekosia.js">
|
|
13
|
+
<img src="https://img.shields.io/npm/dm/nekosia.js" alt="npm downloads">
|
|
14
|
+
</a>
|
|
15
|
+
<a href="https://github.com/Nekosia-API/nekosia.js/issues">
|
|
16
|
+
<img src="https://img.shields.io/github/issues/Nekosia-API/nekosia.js" alt="Issues">
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://github.com/Nekosia-API/nekosia.js/commits/main">
|
|
19
|
+
<img src="https://img.shields.io/github/last-commit/Nekosia-API/nekosia.js" alt="Last commit">
|
|
20
|
+
<img src="https://img.shields.io/github/commit-activity/w/Nekosia-API/nekosia.js" alt="Commit activity">
|
|
21
|
+
<img src="https://img.shields.io/github/languages/code-size/Nekosia-API/nekosia.js" alt="Code size">
|
|
22
|
+
</a>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## ๐ Key Features of the API
|
|
27
|
+
- **Wide range of categories:** Nekosia API offers [virtually every kind of anime graphic](https://nekosia.cat/documentation?page=api-endpoints#main-categories), not limited to neko images.
|
|
28
|
+
- **High image quality:** All images are carefully selected and checked for quality and appropriateness.
|
|
29
|
+
- **Sessions:** The API supports sessions (based on ID or IP address), which helps avoid duplicate images.
|
|
30
|
+
- **Dominant colors:** The API returns a palette of dominant colors for each image.
|
|
31
|
+
- **Image compression:** JSON responses include a link to a compressed image that is much smaller than the original. This can be useful if you want images to load quickly on client devices without sacrificing quality.
|
|
32
|
+
- **Security:** Nekosia API ensures that all provided content is free from NSFW material, making it one of the most trusted sources of anime-themed images.
|
|
33
|
+
|
|
34
|
+
...and thatโs not all!
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## ๐ Own Booru
|
|
38
|
+
Nekosia also offers its own [Booru](https://nekosia.cat/booru), allowing you to browse images returned by the API.
|
|
39
|
+
Users can edit image information, such as tags, which are crucial for us.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## ๐ Documentation
|
|
43
|
+
Check out the [official documentation](https://nekosia.cat/documentation) to learn more.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## ๐ฆ Instalacja
|
|
47
|
+
To install the Nekosia.js module, use the following command:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install neksosia.js
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## ๐ค Lista tagรณw
|
|
55
|
+
You can find the main image categories [here](https://nekosia.cat/documentation?page=api-endpoints#tags-and-categories).
|
|
56
|
+
The full list of tags is available [on the Booru site](https://nekosia.cat/booru/tags).
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## ๐ค How to Use?
|
|
60
|
+
|
|
61
|
+
### Simple Example
|
|
62
|
+
```js
|
|
63
|
+
const { NekosiaAPI } = require('nekosia.js');
|
|
64
|
+
|
|
65
|
+
(async () => {
|
|
66
|
+
const response = await NekosiaAPI.fetchImages('catgirl');
|
|
67
|
+
console.log(response); // Sample response: https://nekosia.cat/documentation?page=api-endpoints#example-response
|
|
68
|
+
})();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
### IP-based Sessions
|
|
73
|
+
In this example, we used an IP-based session. What does this mean? Thanks to this solution, a user with a specific IP address will not encounter duplicates when randomly selecting images.
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
const { NekosiaAPI } = require('nekosia.js');
|
|
77
|
+
|
|
78
|
+
(async () => {
|
|
79
|
+
const response = await NekosiaAPI.fetchImages('catgirl', {
|
|
80
|
+
session: 'ip',
|
|
81
|
+
count: 1,
|
|
82
|
+
additionalTags: [],
|
|
83
|
+
blacklistedTags: []
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
console.log(response);
|
|
87
|
+
})();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### ID-based Sessions
|
|
91
|
+
You can also use `id`, but this will require providing a user identifier (e.g., from Discord). Pass this information in `id` as a string.
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
const { NekosiaAPI } = require('nekosia.js');
|
|
95
|
+
|
|
96
|
+
(async () => {
|
|
97
|
+
const response = await NekosiaAPI.fetchImages('catgirl', {
|
|
98
|
+
session: 'id',
|
|
99
|
+
id: '561621386765971781',
|
|
100
|
+
count: 1,
|
|
101
|
+
additionalTags: [],
|
|
102
|
+
blacklistedTags: []
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
console.log(response);
|
|
106
|
+
})();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### See more
|
|
110
|
+
https://github.com/Nekosia-API/nekosia.js/tree/main/examples
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
## Versions
|
|
114
|
+
```js
|
|
115
|
+
const { NekosiaVersion } = require('nekosia.js');
|
|
116
|
+
|
|
117
|
+
(async () => {
|
|
118
|
+
console.log(NekosiaVersion.module); // Returns the installed module version
|
|
119
|
+
console.log(await NekosiaVersion.api()); // Returns the current API version used by the module
|
|
120
|
+
})();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## โญ ยป Thanks
|
|
125
|
+
If you find the API or this module useful, consider giving a star to the [repository](https://github.com/sefinek24/nekosia.js).
|
|
126
|
+
If you have questions or issues, create a new [Issue](https://github.com/Nekosia-API/nekosia.js/issues/new) or join the [serwer Discord](https://discord.gg/pba76vJhcP).
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
## ๐ ยป MIT License
|
|
130
|
+
Copyright 2023-2024 ยฉ by [Sefinek](https://sefine.net). All rights reserved.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { NekosiaAPI } = require('../index.js');
|
|
2
|
+
|
|
3
|
+
(async () => {
|
|
4
|
+
const response = await NekosiaAPI.fetchImages('foxgirl', {
|
|
5
|
+
session: 'ip',
|
|
6
|
+
count: 1,
|
|
7
|
+
additionalTags: ['cute', 'sakura', 'cherry-blossom'],
|
|
8
|
+
blacklistedTags: ['beret']
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
console.log(response);
|
|
12
|
+
})();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { NekosiaAPI } = require('../index.js');
|
|
2
|
+
|
|
3
|
+
const fetchImages = async (category, options = {}) => {
|
|
4
|
+
try {
|
|
5
|
+
const response = await NekosiaAPI.fetchImages(category, options);
|
|
6
|
+
console.log(`${category.toUpperCase()}:`, response);
|
|
7
|
+
} catch (err) {
|
|
8
|
+
console.error(`Error fetching ${category} images:`, err);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
(async () => {
|
|
13
|
+
await fetchImages('catgirl');
|
|
14
|
+
await fetchImages('foxgirl', { session: 'id', id: 'user123', count: 2 });
|
|
15
|
+
})();
|
package/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const https = require('./services/https.js');
|
|
2
|
+
const BASE_URL = 'https://api.nekosia.cat';
|
|
3
|
+
const API_URL = `${BASE_URL}/api/v1`;
|
|
4
|
+
|
|
5
|
+
class NekosiaAPI {
|
|
6
|
+
buildQueryParams(options = {}) {
|
|
7
|
+
return Object.entries(options)
|
|
8
|
+
.filter(([, value]) => {
|
|
9
|
+
if (typeof value === 'string' && value.includes(',')) {
|
|
10
|
+
throw new Error('A single tag in the string cannot contain commas. Please use an array instead.');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return value != null && value !== '' && (!Array.isArray(value) || value.length > 0);
|
|
14
|
+
})
|
|
15
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${value}`)
|
|
16
|
+
.join('&');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async makeHttpRequest(endpoint) {
|
|
20
|
+
try {
|
|
21
|
+
return await https.get(endpoint);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error(`HTTP request failed for endpoint ${endpoint}: ${err.message}`);
|
|
24
|
+
throw err;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async fetchImages(category, options = {}) {
|
|
29
|
+
if (!category) {
|
|
30
|
+
throw new Error('The image category is required. For example, use fetchImages(\'catgirl\').');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (options.session && !['id', 'ip'].includes(options.session)) {
|
|
34
|
+
throw new Error('The `session` setting can contain only the following values `id` and `ip`, both as strings.');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!options.session && options.id) {
|
|
38
|
+
throw new Error('`id` is not required if the session is `null` or `undefined`');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const queryString = this.buildQueryParams({
|
|
42
|
+
session: null,
|
|
43
|
+
id: null,
|
|
44
|
+
count: 1,
|
|
45
|
+
additionalTags: [],
|
|
46
|
+
blacklistedTags: [],
|
|
47
|
+
...options
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return this.makeHttpRequest(`${API_URL}/images/${category}?${queryString}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async fetchShadowImages(options = {}) {
|
|
54
|
+
if (!Array.isArray(options.additionalTags) || options.additionalTags.length === 0) {
|
|
55
|
+
throw new Error('`additionalTags` must be a non-empty array for the shadow category');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return this.fetchImages('shadow', options);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async fetchById(id) {
|
|
62
|
+
if (!id) throw new Error('`id` parameter is required');
|
|
63
|
+
|
|
64
|
+
return this.makeHttpRequest(`${API_URL}/getImageById/${id}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const NekosiaVersion = {
|
|
69
|
+
module: https.version,
|
|
70
|
+
api: async () => {
|
|
71
|
+
return await https.get(BASE_URL);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
NekosiaAPI: new NekosiaAPI(),
|
|
77
|
+
NekosiaVersion
|
|
78
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nekosia.js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A simple wrapper for the Nekosia API that provides easy access to random anime images. Enhance your projects with the magic of anime and a touch of feline charm meow~~! Discover why switching to Nekosia is the purrfect choice!",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"anime",
|
|
7
|
+
"anime-api",
|
|
8
|
+
"anime-art",
|
|
9
|
+
"anime-characters",
|
|
10
|
+
"anime-gifs",
|
|
11
|
+
"anime-images",
|
|
12
|
+
"anime-library",
|
|
13
|
+
"api-wrapper",
|
|
14
|
+
"cat",
|
|
15
|
+
"cat-ears",
|
|
16
|
+
"catgirl",
|
|
17
|
+
"catgirls",
|
|
18
|
+
"chibi",
|
|
19
|
+
"cute-animals",
|
|
20
|
+
"feline",
|
|
21
|
+
"image-api",
|
|
22
|
+
"japan",
|
|
23
|
+
"javascript",
|
|
24
|
+
"js-library",
|
|
25
|
+
"kawaii",
|
|
26
|
+
"manga",
|
|
27
|
+
"moe",
|
|
28
|
+
"neko",
|
|
29
|
+
"neko-girls",
|
|
30
|
+
"nekos",
|
|
31
|
+
"nekosia",
|
|
32
|
+
"nekosia-api",
|
|
33
|
+
"otaku",
|
|
34
|
+
"random-anime",
|
|
35
|
+
"typescript",
|
|
36
|
+
"waifu"
|
|
37
|
+
],
|
|
38
|
+
"homepage": "https://nekosia.cat",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/Nekosia-API/nekosia.js/issues"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/Nekosia-API/nekosia.js.git"
|
|
45
|
+
},
|
|
46
|
+
"license": "ISC",
|
|
47
|
+
"author": "Sefinek <contact@sefinek.net> (https://sefinek.net)",
|
|
48
|
+
"main": "index.js",
|
|
49
|
+
"typings": "types/index.d.ts",
|
|
50
|
+
"scripts": {
|
|
51
|
+
"test": "jest",
|
|
52
|
+
"up": "ncu -u && npm install && npm update && npm audit fix"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@eslint/js": "^9.9.0",
|
|
56
|
+
"globals": "^15.9.0",
|
|
57
|
+
"jest": "^29.7.0"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const https = require('https');
|
|
2
|
+
const { name, version, homepage, devDependencies } = require('../package.json');
|
|
3
|
+
|
|
4
|
+
const headers = {
|
|
5
|
+
'User-Agent': `Mozilla/5.0 (compatible; ${name}/${version}; +${homepage})${process.env.JEST_WORKER_ID ? ` jest/${devDependencies.jest.replace(/^[^0-9]*/, '')}` : ''}`,
|
|
6
|
+
'Accept': 'application/json',
|
|
7
|
+
'Content-Type': 'application/json',
|
|
8
|
+
'Cache-Control': 'no-cache',
|
|
9
|
+
'Connection': 'keep-alive',
|
|
10
|
+
'DNT': '1'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const makeRequest = url => new Promise((resolve, reject) => {
|
|
14
|
+
const req = https.get(url, { headers, timeout: 9000 }, res => {
|
|
15
|
+
let data = '';
|
|
16
|
+
|
|
17
|
+
res.on('data', chunk => {
|
|
18
|
+
data += chunk;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
res.on('end', () => {
|
|
22
|
+
if ((res.statusCode < 200 || res.statusCode >= 300) && res.statusCode !== 400) {
|
|
23
|
+
return reject(new Error(`HTTP Status Code: ${res.statusCode}`));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const parsedData = JSON.parse(data);
|
|
28
|
+
resolve(parsedData);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
reject(new Error(`Failed to parse JSON: ${err.message}. Response: ${data}`));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
req.on('error', err => reject(new Error(`Request error: ${err.message}`)));
|
|
36
|
+
|
|
37
|
+
req.on('timeout', () => {
|
|
38
|
+
req.destroy();
|
|
39
|
+
reject(new Error('Timeout error'));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
req.end();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
module.exports = { get: makeRequest, version };
|
package/test/api.test.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const { NekosiaAPI } = require('../index.js');
|
|
2
|
+
|
|
3
|
+
describe('NekosiaAPI (API Tests)', () => {
|
|
4
|
+
|
|
5
|
+
describe('fetchImages', () => {
|
|
6
|
+
it('should fetch images for the given category', async () => {
|
|
7
|
+
const res = await NekosiaAPI.fetchImages('catgirl', { count: 1 });
|
|
8
|
+
|
|
9
|
+
expect(res).toBeInstanceOf(Object);
|
|
10
|
+
expect(res.success).toBe(true);
|
|
11
|
+
expect(res.status).toBe(200);
|
|
12
|
+
|
|
13
|
+
expect(res).toHaveProperty('category');
|
|
14
|
+
expect(res).toHaveProperty('id');
|
|
15
|
+
expect(res).toHaveProperty('image');
|
|
16
|
+
|
|
17
|
+
expect(res.image).toHaveProperty('original');
|
|
18
|
+
expect(res.image).toHaveProperty('compressed');
|
|
19
|
+
expect(res.image.original).toHaveProperty('url');
|
|
20
|
+
expect(res.image.original).toHaveProperty('extension');
|
|
21
|
+
expect(res.image.compressed).toHaveProperty('url');
|
|
22
|
+
expect(res.image.compressed).toHaveProperty('extension');
|
|
23
|
+
|
|
24
|
+
expect(res.metadata).toHaveProperty('original');
|
|
25
|
+
expect(res.metadata.original).toHaveProperty('width');
|
|
26
|
+
expect(res.metadata.original).toHaveProperty('height');
|
|
27
|
+
expect(res.metadata.original).toHaveProperty('size');
|
|
28
|
+
expect(res.metadata.original).toHaveProperty('extension');
|
|
29
|
+
|
|
30
|
+
expect(res.metadata).toHaveProperty('compressed');
|
|
31
|
+
expect(res.metadata.compressed).toHaveProperty('width');
|
|
32
|
+
expect(res.metadata.compressed).toHaveProperty('height');
|
|
33
|
+
expect(res.metadata.compressed).toHaveProperty('size');
|
|
34
|
+
expect(res.metadata.compressed).toHaveProperty('extension');
|
|
35
|
+
|
|
36
|
+
expect(res).toHaveProperty('tags');
|
|
37
|
+
expect(res.tags).toBeInstanceOf(Array);
|
|
38
|
+
expect(res.tags.length).toBeGreaterThan(0);
|
|
39
|
+
|
|
40
|
+
expect(res).toHaveProperty('rating');
|
|
41
|
+
expect(res).toHaveProperty('source');
|
|
42
|
+
expect(res.source).toHaveProperty('url');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should return an error for an invalid category', async () => {
|
|
46
|
+
const res = await NekosiaAPI.fetchImages('invalid-category', { count: 1 });
|
|
47
|
+
|
|
48
|
+
expect(res.success).toBe(false);
|
|
49
|
+
expect(res.status).toBe(400);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should return a specified number of images', async () => {
|
|
53
|
+
const res = await NekosiaAPI.fetchImages('catgirl', { count: 3 });
|
|
54
|
+
|
|
55
|
+
expect(res).toBeInstanceOf(Object);
|
|
56
|
+
expect(res.success).toBe(true);
|
|
57
|
+
expect(res.status).toBe(200);
|
|
58
|
+
expect(res.count).toBe(3);
|
|
59
|
+
expect(res).toHaveProperty('images');
|
|
60
|
+
expect(res.images).toBeInstanceOf(Array);
|
|
61
|
+
expect(res.images.length).toBe(3);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('fetchShadowImages', () => {
|
|
66
|
+
it('should handle no images found for shadow category with additional tags', async () => {
|
|
67
|
+
const res = await NekosiaAPI.fetchShadowImages({ count: 1, additionalTags: ['wTbf8J0TirS6a4fO5uyKcRazZOlO5h6o', 'xX9f9pwDAgsM3Li1LwsJ3tXQfGKW4WA0'] });
|
|
68
|
+
|
|
69
|
+
expect(res.success).toBe(false);
|
|
70
|
+
expect(res.status).toBe(400);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should throw an error if additionalTagsArray is empty', async () => {
|
|
74
|
+
await expect(NekosiaAPI.fetchShadowImages([])).rejects.toThrow('`additionalTags` must be a non-empty array for the shadow category');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should return an error response for invalid count parameter', async () => {
|
|
78
|
+
const res = await NekosiaAPI.fetchImages('catgirl', { count: 'invalid' });
|
|
79
|
+
expect(res.success).toBe(false);
|
|
80
|
+
expect(res.status).toBe(400);
|
|
81
|
+
expect(res.message).toBe('Invalid count parameter. Expected a number between 1 and 48.');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('fetchById', () => {
|
|
86
|
+
it('should fetch an image by ID if it exists', async () => {
|
|
87
|
+
const res = await NekosiaAPI.fetchImages('catgirl', { count: 1 });
|
|
88
|
+
|
|
89
|
+
if (res.success && res.id) {
|
|
90
|
+
const id = res.id;
|
|
91
|
+
|
|
92
|
+
const res2 = await NekosiaAPI.fetchById(id);
|
|
93
|
+
expect(res2.success).toBe(true);
|
|
94
|
+
expect(res2.status).toBe(200);
|
|
95
|
+
expect(res2).toHaveProperty('id', id);
|
|
96
|
+
expect(res2).toHaveProperty('image');
|
|
97
|
+
} else {
|
|
98
|
+
throw new Error('No images available to fetch by ID');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should return an error response for invalid ID format', async () => {
|
|
103
|
+
const res = await NekosiaAPI.fetchById('12345');
|
|
104
|
+
expect(res.success).toBe(false);
|
|
105
|
+
expect(res.status).toBe(400);
|
|
106
|
+
expect(res.message).toBe('The image with the provided identifier was not found.');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const { NekosiaAPI } = require('../index.js');
|
|
2
|
+
const https = require('../services/https.js');
|
|
3
|
+
|
|
4
|
+
jest.mock('../services/https.js');
|
|
5
|
+
|
|
6
|
+
describe('NekosiaAPI', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
https.get.mockClear();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe('buildQueryParams', () => {
|
|
12
|
+
it('should correctly build query params', () => {
|
|
13
|
+
const options = { count: 3, additionalTags: ['tag1', 'tag2', 'tag3', 'tag4'], emptyValue: '', nullValue: null };
|
|
14
|
+
const result = NekosiaAPI.buildQueryParams(options);
|
|
15
|
+
expect(result).toBe('count=3&additionalTags=tag1,tag2,tag3,tag4');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('makeHttpRequest', () => {
|
|
20
|
+
it('should make a successful HTTP request', async () => {
|
|
21
|
+
const mockResponse = { data: { results: [] } };
|
|
22
|
+
https.get.mockResolvedValue(mockResponse);
|
|
23
|
+
|
|
24
|
+
const endpoint = 'https://api.nekosia.cat/test-endpoint';
|
|
25
|
+
const res = await NekosiaAPI.makeHttpRequest(endpoint);
|
|
26
|
+
|
|
27
|
+
expect(res).toEqual(mockResponse);
|
|
28
|
+
expect(https.get).toHaveBeenCalledWith(endpoint);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should handle HTTP request errors', async () => {
|
|
32
|
+
const mockError = new Error('Request failed');
|
|
33
|
+
https.get.mockRejectedValue(mockError);
|
|
34
|
+
|
|
35
|
+
const endpoint = 'https://api.nekosia.cat/test-endpoint';
|
|
36
|
+
|
|
37
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
38
|
+
|
|
39
|
+
await expect(NekosiaAPI.makeHttpRequest(endpoint)).rejects.toThrow('Request failed');
|
|
40
|
+
expect(https.get).toHaveBeenCalledWith(endpoint);
|
|
41
|
+
|
|
42
|
+
consoleErrorSpy.mockRestore();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('fetchImages', () => {
|
|
47
|
+
it('should build correct endpoint and make request for given category', async () => {
|
|
48
|
+
const mockResponse = { data: { results: [] } };
|
|
49
|
+
https.get.mockResolvedValue(mockResponse);
|
|
50
|
+
|
|
51
|
+
const expectedEndpoint = 'https://api.nekosia.cat/api/v1/images/catgirl?count=2&additionalTags=cute';
|
|
52
|
+
const res = await NekosiaAPI.fetchImages('catgirl', { count: 2, additionalTags: 'cute' });
|
|
53
|
+
|
|
54
|
+
expect(res).toEqual(mockResponse);
|
|
55
|
+
expect(https.get).toHaveBeenCalledWith(expectedEndpoint);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('fetchShadowImages', () => {
|
|
60
|
+
it('should throw an error if additionalTags is empty', async () => {
|
|
61
|
+
await expect(NekosiaAPI.fetchShadowImages({})).rejects.toThrow('`additionalTags` must be a non-empty array for the shadow category');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should correctly call fetchImages with additionalTags', async () => {
|
|
65
|
+
const mockResponse = { data: { results: [] } };
|
|
66
|
+
https.get.mockResolvedValue(mockResponse);
|
|
67
|
+
|
|
68
|
+
const expectedEndpoint = 'https://api.nekosia.cat/api/v1/images/shadow?count=1&additionalTags=dark,shadow';
|
|
69
|
+
const res = await NekosiaAPI.fetchShadowImages({ count: 1, additionalTags: ['dark', 'shadow'] });
|
|
70
|
+
|
|
71
|
+
expect(res).toEqual(mockResponse);
|
|
72
|
+
expect(https.get).toHaveBeenCalledWith(expectedEndpoint);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('fetchById', () => {
|
|
77
|
+
it('should throw an error if id is not provided', async () => {
|
|
78
|
+
await expect(NekosiaAPI.fetchById()).rejects.toThrow('`id` parameter is required');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should correctly fetch image by ID', async () => {
|
|
82
|
+
const mockResponse = { data: { id: '123' } };
|
|
83
|
+
https.get.mockResolvedValue(mockResponse);
|
|
84
|
+
|
|
85
|
+
const id = '123';
|
|
86
|
+
const res = await NekosiaAPI.fetchById(id);
|
|
87
|
+
|
|
88
|
+
expect(res).toEqual(mockResponse);
|
|
89
|
+
expect(https.get).toHaveBeenCalledWith(`https://api.nekosia.cat/api/v1/getImageById/${id}`);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|