├── .eslintrc.js ├── .github └── workflows │ ├── contributors.yml │ └── main.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── PAGE.md ├── USAGE.md ├── optionTypes.md ├── resultTypes.md └── wiki.md ├── jest.config.js ├── package.json ├── source ├── errors.ts ├── index.ts ├── messages.ts ├── optionTypes.ts ├── page.ts ├── request.ts ├── resultTypes.ts └── utils.ts ├── test ├── categories.test.ts ├── citation.test.ts ├── content.test.ts ├── coordinates.test.ts ├── featuredContent.test.ts ├── html.test.ts ├── images.test.ts ├── index.test.ts ├── infobox.test.ts ├── intro.test.ts ├── langlinks.test.ts ├── links.test.ts ├── media.test.ts ├── mobileHtml.test.ts ├── onThisDay.test.ts ├── pdf.test.ts ├── random.test.ts ├── references.test.ts ├── related.test.ts ├── request.test.ts ├── samples.ts ├── summary.test.ts ├── tables.test.ts └── utils.test.ts ├── tsconfig.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", // Specifies the ESLint parser 3 | plugins: [ 4 | "@typescript-eslint/eslint-plugin", 5 | "eslint-plugin-tsdoc" 6 | ], 7 | parserOptions: { 8 | ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features 9 | sourceType: "module" // Allows for the use of imports 10 | }, 11 | extends: [ 12 | "plugin:@typescript-eslint/recommended" // Uses the recommended rules from the @typescript-eslint/eslint-plugin 13 | ], 14 | rules: { 15 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs 16 | // e.g. "@typescript-eslint/explicit-function-return-type": "off", 17 | "@typescript-eslint/explicit-module-boundary-types": "off", 18 | "@typescript-eslint/no-explicit-any": "off", 19 | "tsdoc/syntax": "warn" 20 | } 21 | }; -------------------------------------------------------------------------------- /.github/workflows/contributors.yml: -------------------------------------------------------------------------------- 1 | name: contributors 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | contrib-readme-job: 8 | runs-on: ubuntu-latest 9 | name: A job to automate contrib in readme 10 | steps: 11 | - name: Contribute List 12 | uses: akhilmhdh/contributors-readme-action@v2.3.6 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | branches: 5 | - master 6 | tags: 7 | - '!*' # Do not execute on tags 8 | paths: 9 | - source/* 10 | - test/* 11 | - '*.json' 12 | - package.lock 13 | - .github/workflows/* 14 | pull_request: 15 | 16 | jobs: 17 | build: 18 | name: Node.js ${{ matrix.node-version }} 19 | runs-on: ubuntu-latest 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | node-version: 24 | - 14 25 | - 16 26 | - 18 27 | steps: 28 | - uses: actions/checkout@v2 29 | - uses: actions/setup-node@v1 30 | with: 31 | node-version: ${{ matrix.node-version }} 32 | - run: yarn 33 | - run: yarn build 34 | coverage: 35 | name: coverage 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@master 39 | - uses: actions/setup-node@master 40 | with: 41 | node-version: '16' 42 | - run: npm install 43 | - run: npm run test 44 | - uses: paambaati/codeclimate-action@v3.2.0 45 | env: 46 | CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} 47 | if: env.CC_TEST_REPORTER_ID != null 48 | with: 49 | coverageCommand: yarn test 50 | debug: true 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | test.js 5 | .DS_Store -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at gvind4@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We welcome patches and features. There are however a few things that are 4 | required before your pull request can be merged. Make sure you cut a branch 5 | from develop and all PRs pointed towards master. 6 | 7 | # Tests 8 | 9 | ## How to write a new test case 10 | 11 | For any new feature added, we expect a new test case. Since the repo has a 100% code coverage, we expect a success case test and a failure case test for each case. Read up more about why unit testing [here](https://dzone.com/articles/top-8-benefits-of-unit-testing). 12 | 13 | ## Running test cases 14 | 15 | Make sure your PR passes all the tests by running `npm run test` before 16 | raising a PR. 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Govind S 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WIKIPEDIA [![build](https://github.com/dopecodez/Wikipedia/workflows/build/badge.svg)](https://github.com/dopecodez/Wikipedia/actions) [![Test Coverage](https://api.codeclimate.com/v1/badges/a44c826dbef8c7f5ea45/test_coverage)](https://codeclimate.com/github/dopecodez/Wikipedia/test_coverage) [![Contributions](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dopecodez/Wikipedia/issues) [![npm version](https://badge.fury.io/js/wikipedia.svg)](https://badge.fury.io/js/wikipedia) 2 | 3 | Wikipedia for node. Works in the browser as well. 4 | 5 | Implements [legacy](https://www.mediawiki.org/wiki/API:Main_page) wiki endpoints and also the newer 6 | [REST API](https://en.wikipedia.org/api/rest_v1/#/). 7 | 8 | Try out the new [`summary()`][9] REST endpoint for a introduction to your page and the main images optimized for browsers and mobile! 9 | 10 | You can also now get the events which happened on a particular day using the [`onThisDay()`][8] api, which supports filtering by [event types][7] as well. 11 | 12 | Built with latest ES6 and native support for async/await and promises. 13 | 14 | Built with TypeScript - exports all the types used. 15 | 16 | # INSTALLATION 17 | 18 | ``` 19 | $ npm install wikipedia 20 | ``` 21 | 22 | ## Highlights 23 | 24 | - [What can it do](#what-can-it-do) 25 | - [Usage](#Usage) 26 | - [Options](#Options) 27 | - [Result Types](#result-types) 28 | - [Contributing](#contributing) 29 | 30 | For detailed documentation of methods available on `wiki` and `page`, 31 | - [wiki][4] 32 | - [Page][2] 33 | 34 | ## What can it do? 35 | 36 | - Get a summary for a page which contains the intro and main image optimized for web and mobile with the new wikipedia [REST APIs](https://en.wikipedia.org/api/rest_v1/#/) 37 | - Fetch article content 38 | - Find all links/images/categories in a page 39 | - Gets all the relevant events that happened on a particular day. You can further filter this by [event type][7] 40 | - Find related articles from the given article 41 | - Find articles by geographical location 42 | - Get a wikipedia page as a pdf document 43 | - Supports switching languages 44 | - Parses infoboxes using [infobox-parser](https://github.com/dijs/infobox-parser) 45 | 46 | ## Usage 47 | 48 | ```js 49 | const wiki = require('wikipedia'); 50 | 51 | (async () => { 52 | try { 53 | const page = await wiki.page('Batman'); 54 | console.log(page); 55 | //Response of type @Page object 56 | const summary = await page.summary(); 57 | console.log(summary); 58 | //Response of type @wikiSummary - contains the intro and the main image 59 | } catch (error) { 60 | console.log(error); 61 | //=> Typeof wikiError 62 | } 63 | })(); 64 | ``` 65 | The page method returns a [Page][2] class object which has fields like `pageid`, `title`, `parentid`, `revisionid` and methods like `summary()`, `intro()`, `images()`, `html()` and more. 66 | 67 | All the page methods can take a title parameter or a pageId. Read up on the [Page documentation][2] here to see a detailed overview of the methods available in page. 68 | 69 | You can also call methods like `summary()` on the `wiki` object directly. [Read up here][3] to see when you should use the `page` object and when you should call `summary()` directly. There's a performance difference! Long story short, use the method directly if you are using only the `summary` of the page and are not expecting to use any of the other `page` attributes. 70 | 71 | ```js 72 | const wiki = require('wikipedia'); 73 | 74 | (async () => { 75 | try { 76 | const summary = await wiki.summary('Batman'); 77 | console.log(summary); 78 | //Response of type @wikiSummary - contains the intro and the main image 79 | } catch (error) { 80 | console.log(error); 81 | //=> Typeof wikiError 82 | } 83 | })(); 84 | ``` 85 | You can now get the events which happened on a particular day using the new `onThisDay()` api on the wiki object. 86 | 87 | ```js 88 | const wiki = require('wikipedia'); 89 | 90 | (async () => { 91 | try { 92 | const events = await wiki.onThisDay(); 93 | const deaths = await wiki.onThisDay({type:'deaths', month:'2', day:'28'}); 94 | console.log(events); // returns all the events which happened today 95 | console.log(deaths); // returns all deaths which happened on Feb 28 96 | } catch (error) { 97 | console.log(error); 98 | //=> Typeof wikiError 99 | } 100 | })(); 101 | ``` 102 | 103 | There are other methods like `search()`, `geoSearch()`, `suggest()`, `setLang()`, `setUserAgent()` which should be called on the wiki object directly. Read up on the [wiki documentation][4] to see a complete list of methods available on the wiki default object. 104 | 105 | ```js 106 | const wiki = require('wikipedia'); 107 | 108 | (async () => { 109 | try { 110 | const searchResults = await wiki.search('Batma'); 111 | console.log(searchResults); 112 | //Response of type @wikiSearchResult - contains results and optionally a suggestion 113 | const newUrl = await wiki.setLang('fr'); 114 | console.log(newUrl); 115 | //Returns the api url with language changed - use `languages()` method to see a list of available langs 116 | } catch (error) { 117 | console.log(error); 118 | //=> Typeof wikiError 119 | } 120 | })(); 121 | ``` 122 | 123 | You can export types or even specific methods if you are using modern ES6 js or TypeScript. 124 | ```js 125 | import wiki from 'wikipedia'; 126 | import { wikiSummary, summaryError } from 'wikipedia'; 127 | import { summary } from 'wikipedia'; 128 | 129 | (async () => { 130 | try { 131 | let summary: wikiSummary; //sets the object as type wikiSummary 132 | summary = await wiki.summary('Batman'); 133 | console.log(summary); 134 | let summary2 = await summary('Batman');//using summary directly 135 | } catch (error) { 136 | console.log(error); 137 | //=> Typeof summaryError, helpful in case you want to handle this error separately 138 | } 139 | })(); 140 | ``` 141 | 142 | ## Options 143 | 144 | All methods have options you can pass them. You can find them in [optionTypes documentation][5]. 145 | 146 | ## Result Types 147 | 148 | All the returned result types are documented as well. You can find them [here][6]. 149 | 150 | ## Contributing 151 | 152 | Before opening a pull request please make sure your changes follow the [contribution guidelines][1]. 153 | 154 | 155 | ## Contributors 156 | 157 | The project would not be the way it is without these rockstars. 158 | 159 | 160 | 161 | 162 | 169 | 176 | 183 | 190 | 197 | 204 | 205 | 212 | 219 | 226 | 233 |
163 | 164 | dopecodez 165 |
166 | Govind S 167 |
168 |
170 | 171 | friendofdog 172 |
173 | Kevin Kee 174 |
175 |
177 | 178 | bumbummen99 179 |
180 | Patrick 181 |
182 |
184 | 185 | gtibrett 186 |
187 | Brett 188 |
189 |
191 | 192 | 0xflotus 193 |
194 | 0xflotus 195 |
196 |
198 | 199 | Greeshmareji 200 |
201 | Greeshma R 202 |
203 |
206 | 207 | Jman1868 208 |
209 | Jamal Ahmed 210 |
211 |
213 | 214 | zoetrope69 215 |
216 | Z 217 |
218 |
220 | 221 | bigmistqke 222 |
223 | Bigmistqke 224 |
225 |
227 | 228 | yg-i 229 |
230 | Null 231 |
232 |
234 | 235 | 236 | [1]: https://github.com/dopecodez/wikipedia/blob/master/CONTRIBUTING.md 237 | [2]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md 238 | [3]: https://github.com/dopecodez/wikipedia/blob/master/docs/USAGE.md#when-to-use-page 239 | [4]: https://github.com/dopecodez/wikipedia/blob/master/docs/wiki.md 240 | [5]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md 241 | [6]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md 242 | [7]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md#eventOptions 243 | [8]: https://github.com/dopecodez/wikipedia/blob/master/docs/wiki.md#onThisDay 244 | [9]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#summary 245 | -------------------------------------------------------------------------------- /docs/PAGE.md: -------------------------------------------------------------------------------- 1 | # Page 2 | 3 | ## Highlights 4 | 5 | - [Class Members](#class-members) 6 | - [Functions](#functions) 7 | - [summary()](#summary) 8 | - [intro()](#intro) 9 | - [images()](#images) 10 | - [html()](#html) 11 | - [related()](#related) 12 | - [media()](#media) 13 | - [content()](#content) 14 | - [categories()](#categories) 15 | - [links()](#links) 16 | - [coordinates()](#coordinates) 17 | - [references()](#references) 18 | - [langLinks()](#langLinks) 19 | - [mobileHtml()](#mobileHtml) 20 | - [pdf()](#pdf) 21 | 22 | ## Class Members 23 | 24 | The `Page` class has the following properties: 25 | 26 | ```js 27 | pageid!: number; // id of page 28 | ns!: number; 29 | title!: string; // Title of page 30 | contentmodel!: string; 31 | pagelanguage!: string; // The language of the page 32 | pagelanguagehtmlcode!: string; 33 | pagelanguagedir!: string; 34 | touched!: string; 35 | lastrevid!: number; //The last rev id of the page 36 | length!: number; 37 | fullurl!: string; // The full url of the page 38 | editurl!: string; 39 | canonicalurl!: string; 40 | revid!: number; // The revision id of the page 41 | parentid!: number; // The parent id of the page 42 | _summary!: wikiSummary; // Stores the summary in case it is preloaded or called on the page object earlier 43 | _images!: Array; // Stores the images in case it is preloaded or called on the page object earlier 44 | _content!: string; // Stores the content in case it is preloaded or called on the page object earlier 45 | _html!: string; // Stores the html in case it is preloaded or called on the page object earlier 46 | _categories!: Array; // Stores the categories in case it is preloaded or called on the page object earlier 47 | _references!: Array; // Stores the references in case it is preloaded or called on the page object earlier 48 | _links!: Array; // Stores the links in case it is preloaded or called on the page object earlier 49 | _coordinates!: coordinatesResult; // Stores the coordinates in case it is preloaded or called on the page object earlier 50 | _langLinks!: Array; // Stores the langLinks in case it is preloaded or called on the page object earlier 51 | _infobox!: any; // Stores the infobox in case it is preloaded or called on the page object earlier 52 | _tables!: Array; // Stores the tables in case it is preloaded or called on the page object earlier 53 | _intro!: string; // Stores the intro in case it is preloaded or called on the page object earlier 54 | _related!: Array; // Stores the related info in case it is preloaded or called on the page object earlier 55 | ``` 56 | 57 | ## Functions 58 | 59 | The page object has the following methods available on it : 60 | 61 | #### summary() 62 | 63 | Returns the summary for the page as [wikiSummary][2] object. Summary contains the title, page Id, introduction, main image and content urls for the page. 64 | 65 | ```js 66 | summary(pageOptions: pageOptions | undefined): Promise 67 | ``` 68 | - @param [pageOptions][1] - the options for the page 69 | - @result[wikiSummary][2] - the summary object for the wiki page 70 | 71 | ```js 72 | //example 73 | const page = await wiki.page('Batman'); 74 | const summary = await page.summary({redirect: false}); 75 | ``` 76 | 77 | #### intro() 78 | 79 | Returns the introduction of the page as string 80 | 81 | ```js 82 | intro(pageOptions: pageOptions | undefined): Promise 83 | ``` 84 | - @param [pageOptions][1] - the options for the page 85 | 86 | ```js 87 | //example 88 | const page = await wiki.page('Batman'); 89 | const intro = await page.intro({redirect: false}); 90 | ``` 91 | 92 | #### images() 93 | 94 | Returns the images present in the page. 95 | 96 | **For a main image, use the summary endpoint** 97 | 98 | ```js 99 | images(listOptions: listOptions | undefined): Promise> 100 | ``` 101 | - @param [listOptions][3] - the options for the page 102 | - @result [imageResult][4] - the image results for the page 103 | 104 | ```js 105 | //example 106 | const page = await wiki.page('Batman'); 107 | const images = await page.images({redirect: true, limit: 5}); 108 | ``` 109 | 110 | #### html() 111 | 112 | Returns the html content of the page as a string 113 | 114 | ```js 115 | html = async (pageOptions?: pageOptions): Promise 116 | ``` 117 | - @param [pageOptions][1] - the options for the page 118 | 119 | ```js 120 | //example 121 | const page = await wiki.page('Batman'); 122 | const html = await page.html({redirect: false}); 123 | ``` 124 | 125 | #### related() 126 | 127 | Returns the related pages given for a a page. 128 | 129 | ```js 130 | related = async (pageOptions?: pageOptions): Promise 131 | ``` 132 | - @param [pageOptions][1] - the options for the page 133 | - @result[wikiSummary][2] - the summary object for the wiki page 134 | 135 | ```js 136 | //example 137 | const page = await wiki.page('Batman'); 138 | const related = await page.related({redirect: false}); 139 | ``` 140 | 141 | #### media() 142 | 143 | Gets the list of media items present in the page 144 | 145 | ```js 146 | media = async (pageOptions?: pageOptions): Promise 147 | ``` 148 | - @param [pageOptions][1] - the options for the page 149 | - @result[wikiMediaResult][6] - the media result object for the wiki page 150 | 151 | ```js 152 | //example 153 | const page = await wiki.page('Batman'); 154 | const media = await page.media({redirect: false}); 155 | ``` 156 | 157 | #### content() 158 | 159 | Returns the plain text content of a page. 160 | 161 | ```js 162 | content = async (pageOptions?: pageOptions): Promise 163 | ``` 164 | - @param [pageOptions][1] - the options for the page 165 | 166 | ```js 167 | //example 168 | const page = await wiki.page('Batman'); 169 | const content = await page.content({redirect: false}); 170 | ``` 171 | 172 | #### categories() 173 | 174 | Returns the categories as an array of string 175 | 176 | ```js 177 | categories = async (listOptions?: listOptions): Promise> 178 | ``` 179 | - @param [listOptions][3] - the options for the page 180 | 181 | ```js 182 | //example 183 | const page = await wiki.page('Batman'); 184 | const categories = await page.categories({redirect: false, , limit: 5}); 185 | ``` 186 | 187 | #### links() 188 | 189 | Returns the links present in the page 190 | 191 | ```js 192 | links = async (listOptions?: listOptions): Promise> 193 | ``` 194 | - @param [listOptions][3] - the options for the page 195 | 196 | ```js 197 | //example 198 | const page = await wiki.page('Batman'); 199 | const links = await page.links({redirect: false, , limit: 5}); 200 | ``` 201 | 202 | #### references() 203 | 204 | Returns the references and external links in a page. 205 | 206 | ```js 207 | references = async (listOptions?: listOptions): Promise> 208 | ``` 209 | - @param [listOptions][3] - the options for the page 210 | 211 | ```js 212 | //example 213 | const page = await wiki.page('Batman'); 214 | const references = await page.references({redirect: false, , limit: 5}); 215 | ``` 216 | 217 | #### coordinates() 218 | 219 | Returns the coordinates of a page 220 | 221 | ```js 222 | coordinates = async (pageOptions?: pageOptions): Promise 223 | ``` 224 | - @param [pageOptions][1] - the options for the page 225 | - @result[coordinatesResult][5] - the coordinates result object for the wiki page 226 | 227 | ```js 228 | //example 229 | const page = await wiki.page('Batman'); 230 | const coordinates = await page.coordinates({redirect: false}); 231 | ``` 232 | 233 | #### langLinks() 234 | 235 | Returns the language links present in the page 236 | 237 | ```js 238 | langLinks = async (listOptions?: listOptions): Promise> 239 | ``` 240 | - @param [listOptions][3] - the options for the page 241 | - @result[langLinksResult][6] - the langLinks result object 242 | 243 | ```js 244 | //example 245 | const page = await wiki.page('Batman'); 246 | const langLinks = await page.langLinks({redirect: false, , limit: 5}); 247 | ``` 248 | 249 | #### infobox() 250 | 251 | The infobox data(if present), as a JSON object. 252 | 253 | ```js 254 | infobox = async (pageOptions?: pageOptions): Promise 255 | ``` 256 | - @param [pageOptions][1] - the options for the page 257 | 258 | ```js 259 | //example 260 | const page = await wiki.page('Batman'); 261 | const info = await page.infobox({redirect: false}); 262 | ``` 263 | 264 | #### tables() 265 | 266 | The tables data in the page, if present as an array of json objects. 267 | ```js 268 | tables = async (pageOptions?: pageOptions): Promise> 269 | ``` 270 | - @param [pageOptions][1] - the options for the page 271 | 272 | ```js 273 | //example 274 | const page = await wiki.page('Batman'); 275 | const tables = await page.tables({redirect: false}); 276 | ``` 277 | 278 | ### mobileHtml() 279 | 280 | Returns mobile-optimised HTML for a wiki page, given a title. Follows redirects by default. 281 | 282 | ```js 283 | mobileHtml = async (redirect?: boolean): Promise 284 | ``` 285 | - @param redirect - whether to redirect in case of 302 286 | 287 | ```js 288 | const page = await wiki.page('John_Locke'); 289 | const htmlJohnLocke = page.mobileHtml(); 290 | console.log(htmlJohnLocke); // displays HTML for John Locke's wiki page 291 | const page = await wiki.page('Disco_Stu'); 292 | const htmlDiscoStu = page.mobileHtml("Disco_Stu"); 293 | console.log(htmlDiscoStu); // redirects to List of recurring The Simpsons characters 294 | const page = await wiki.page('Disco_Stu', {redirect: false}); 295 | console.log(page); // no result, as redirect is false 296 | ``` 297 | 298 | ### pdf() 299 | 300 | Returns the wikipedia link to pdf. Valid `format` are `a4`. `letter`, `legal`. Valid `type` are `desktop`, `mobile`.(by default fetches `a4` for `desktop`). 301 | 302 | ```js 303 | pdf = async (pdfOptions?: pdfOptions): Promise 304 | ``` 305 | - @param [pdfOptions][7] - the options for the pdf 306 | 307 | ```js 308 | //example 309 | const page = await wiki.page('Batman'); 310 | const pdf = await page.pdf({type:'mobile', format: 'legal'}); 311 | ``` 312 | 313 | [1]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md#pageOptions 314 | [2]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#wikiSummary 315 | [3]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md#listOptions 316 | [4]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#imageResult 317 | [5]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#coordinatesResult 318 | [5]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#langLinksResult 319 | [6]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#wikiMediaResult 320 | [7]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md#pdfOptions 321 | -------------------------------------------------------------------------------- /docs/USAGE.md: -------------------------------------------------------------------------------- 1 | # Usage Recommendations 2 | 3 | ## Highlights 4 | 5 | - [When to use page](#when-to-use-page) 6 | 7 | ## When to use page 8 | 9 | All the page functions like `summary()`, `images()`, `html()`(and the rest!) can be called directly from the `wiki` object. Note here that both the functions will output the same result! 10 | 11 | ```js 12 | const wiki = require('wikipedia'); 13 | 14 | (async () => { 15 | try { 16 | const page = await wiki.page('Batman'); 17 | console.log(response); 18 | //Response of type @Page object 19 | const summaryThroughPage = await page.summary(); 20 | console.log(summary); 21 | const summaryWithoutPage = await wiki.summary('Batman'); 22 | console.log(summary); 23 | // summaryThroughPage = summaryWithoutPage 24 | //Response of type @wikiSummary - contains the intro and the main image 25 | } catch (error) { 26 | console.log(error); 27 | //=> Typeof wikiError 28 | } 29 | })(); 30 | ``` 31 | Internally, if you call the function directly on the `wiki` object, theres only one API being called while if you call the `page()` and then `summary()`, it'll be two APIs called. 32 | 33 | **When to use the `page()` method?** Only use the `page()` method if you are going to use multiple attributes in the same page. Once you have a `Page` object, the loading of fields in this will be faster, so make sure you're using `page()` if you want to use multiple attributes within a page. Page objects are also helpful in case you want to `preload` some fields, and get the attributes you want to use along with the page object itself. 34 | 35 | -------------------------------------------------------------------------------- /docs/optionTypes.md: -------------------------------------------------------------------------------- 1 | # Input Options 2 | 3 | ## Highlights 4 | 5 | - [pageOptions](#pageOptions) 6 | - [listOptions](#listOptions) 7 | - [searchOptions](#searchOptions) 8 | - [geoOptions](#geoOptions) 9 | - [eventOptions](#eventOptions) 10 | - [fcOptions](#fcOptions) 11 | - [pdfOptions](#pdfOptions) 12 | - [autocompletionOptions](#autocompletionoptions) 13 | 14 | ### pageOptions 15 | 16 | ```js 17 | interface pageOptions { 18 | autoSuggest?: boolean 19 | redirect?: boolean 20 | preload?: boolean 21 | fields?: Array 22 | } 23 | ``` 24 | The options for page methods on `Page` or `wiki`. Generally, can be used for all page methods which do not return a array. 25 | 26 | - `redirect : boolean`(default = true) - redirect in case wikipedia returns a 304. **This is the only field that is applicable to any method called on a [Page][1] object**. 27 | - `autoSuggest : boolean`(default = false) - suggest a page title which is reccomened by wikipedia for given string. Useful in case you are using user input to get details for a page. 28 | 29 | The other two fields are only applicable if added on the `wiki.page('title')` method, otherwise its ignored. 30 | 31 | - `preload : boolean`(default = false) - When used with fields, it can preload any fields for the page(Loads summary by default if used without `fields` argument). This is helpful in case you want the whole loading to take place in one single step instead of calling each field when required. 32 | - `fields : string[]` - Accepts a array of string which should a combination the functions available on page(Accepted values are combinations of: 'summary','images','intro','html,'content','categories','links','references','coordinates','langLinks','infobox','tables','related'). 33 | 34 | ### listOptions 35 | 36 | ```js 37 | interface listOptions { 38 | autoSuggest?: boolean 39 | redirect?: boolean 40 | limit?: number 41 | } 42 | ``` 43 | The options for page methods on `Page` or `wiki`. Generally, can be used for all page methods which return an array 44 | 45 | - `autoSuggest : boolean`(default = false) - suggest a page title which is reccomened by wikipedia for given search string* 46 | - `redirect : boolean`(default = true) - redirect in case wikipedia returns a 304 47 | - `limit : number`(default = 10) - Use this to increase/decrease number of results in the returned array 48 | 49 | ### searchOptions 50 | 51 | ```js 52 | interface searchOptions { 53 | limit?: number // the number of results 54 | suggestion?: boolean // Loads the autosuggested value along with search results 55 | } 56 | ``` 57 | - `limit : number`(default = 10) - Use this to increase/decrease number of results in the returned search result array 58 | - `suggestion: boolean`(default=false) - If set to true, returns the auto suggested page along with search results 59 | 60 | ### geoOptions 61 | ```js 62 | interface geoOptions { 63 | limit?: number 64 | radius?: number 65 | } 66 | ``` 67 | - `limit : number`(default = 10) - Use this to increase/decrease number of results in the returned geo result array 68 | - `radius: number`(default = 1000) - Search radius in meters 69 | 70 | ### eventOptions 71 | ```js 72 | interface eventOptions { 73 | type?: eventTypes, 74 | month?: string, 75 | day?: string 76 | } 77 | ``` 78 | The options for the onThisDay function on wiki object. 79 | 80 | - `type : eventTypes`(default = all) - Accepts types of valid events on the wikipedia Rest API. Events can be one of `all`, `selected`, `births`, `deaths`, `events`, `holidays`. 81 | - `month : string`(default = date.getMonth()) - Use this to pass the month you want as a string. By default, it will take current month. 82 | - `day : string`(default = date.getDay()) - Use this to pass the day you want as a string. By default, it will take current day. 83 | 84 | ### fcOptions 85 | ```js 86 | interface fcOptions { 87 | year?: string, 88 | month?: string, 89 | day?: string 90 | } 91 | ``` 92 | The options for the featured content function on wiki object. 93 | 94 | - `year : string`(default = date.getYear()) - Use this to pass the year you want as a string. By default, it will take current year. 95 | - `month : string`(default = date.getMonth()) - Use this to pass the month you want as a string. By default, it will take current month. 96 | - `day : string`(default = date.getDay()) - Use this to pass the day you want as a string. By default, it will take current day. 97 | 98 | ### pdfOptions 99 | ```js 100 | interface pdfOptions { 101 | autoSuggest?: boolean 102 | format?: format 103 | type?: pdfType 104 | } 105 | ``` 106 | The options for the onThisDay function on wiki object. 107 | 108 | - `type : pdfType`(default = a4) - type can be one of `a4`, `letter`, `legal`. 109 | - `format : format`(default = desktop) - format can be `desktop` or `mobile`. 110 | - `autoSuggest : boolean`(default = false) - suggest a page title which is reccomened by wikipedia for given search string* 111 | 112 | ### autocompletionOptions 113 | ```js 114 | interface autocompletionOptions { 115 | limit?: number 116 | } 117 | ``` 118 | The options for the autocompletions function on wiki object. 119 | 120 | - `limit : number`(default = 10) - Use this to increase/decrease number of results in the returned array 121 | 122 | [1]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#functions -------------------------------------------------------------------------------- /docs/resultTypes.md: -------------------------------------------------------------------------------- 1 | # Result Types 2 | 3 | ## Highlights 4 | 5 | - [wikiSummary](#wikiSummary) 6 | - [imageResult](#imageResult) 7 | - [coordinatesResult](#coordinatesResult) 8 | - [langLinksResult](#langLinksResult) 9 | - [wikiSearchResult](#wikiSearchResult) 10 | - [geoSearchResult](#geoSearchResult) 11 | - [languageResult](#languageResult) 12 | - [wikiMediaResult](#wikiMediaResult) 13 | - [eventResult](#eventResult) 14 | - [fcResult](#fcResult) 15 | - [relatedResult](#relatedResult) 16 | - [titleItem](#titleItem) 17 | - [mobileSections](#mobileSections) 18 | 19 | ### wikiSummary 20 | 21 | The wiki summary for the page. 22 | 23 | ```js 24 | interface wikiSummary { 25 | type: string, 26 | title: string, // title of the page 27 | displaytitle: string, // title of the page 28 | namespace: { id: number, text: string }, 29 | wikibase_item: string, 30 | titles: { canonical: string, normalized: string, display: string }, 31 | pageid: number, // page Id 32 | thumbnail: { // Thumbnail for the page 33 | source: string, 34 | width: number, 35 | height: number 36 | }, 37 | originalimage: { 38 | source: string, 39 | width: number, 40 | height: number 41 | }, 42 | lang: string, 43 | dir: string, 44 | revision: string, 45 | tid: string, 46 | timestamp: string, 47 | description: string, 48 | description_source: string, 49 | content_urls: { // The urls will be here 50 | desktop: { 51 | page: string, 52 | revisions: string, 53 | edit: string, 54 | talk: string 55 | }, 56 | mobile: { 57 | page: string, 58 | revisions: string, 59 | edit: string, 60 | talk: string 61 | } 62 | }, 63 | extract: string, 64 | extract_html: string, 65 | normalizedtitle?: string, 66 | coordinates?: { 67 | lat: number, 68 | lon: number 69 | } 70 | } 71 | ``` 72 | 73 | ### imageResult 74 | 75 | The image result for the page. 76 | 77 | ```js 78 | interface imageResult { 79 | pageid: number, 80 | ns: number, 81 | title: string, 82 | imagerepository: string, 83 | imageinfo: any, 84 | url: string // This will contain the image url 85 | } 86 | ``` 87 | 88 | ### coordinatesResult 89 | 90 | The coordinates result. 91 | ```js 92 | interface coordinatesResult { 93 | lat: number //latitude 94 | lon: number, //longitude 95 | primary: string, 96 | globe: string // The globe ie Earth! 97 | } 98 | ``` 99 | 100 | ### langLinksResult 101 | 102 | The lang links result 103 | 104 | ```js 105 | interface langLinksResult { 106 | lang: string, //language code 107 | title: string, // The title of the page 108 | url: string // url of the page 109 | } 110 | ``` 111 | 112 | ### wikiSearchResult 113 | 114 | The search result. The autosuggest will be populated or null based on input params. 115 | 116 | ```js 117 | interface wikiSearchResult { 118 | results: any[], // the search results as an array 119 | suggestion: string // the suggestion string if suggestion option is set to true 120 | } 121 | ``` 122 | ### geoSearchResult 123 | 124 | The geosearch result. 125 | 126 | ```js 127 | interface geoSearchResult { 128 | pageid: number, // the page id. use this or title for further requests 129 | ns: number, 130 | title: string, // the title. use this or pageid for further requests 131 | lat: number, // the page latitude 132 | lon: number, // the page longitude 133 | dist: number, 134 | primary: string, 135 | type: string // the type of the page eg: city, museum 136 | } 137 | ``` 138 | ### languageResult 139 | 140 | ```js 141 | interface languageResult { 142 | [key: string]: string // key will be the language code to be used in setLang(key), and the value will be the full url 143 | } 144 | ``` 145 | 146 | ### wikiMediaResult 147 | 148 | ```js 149 | interface wikiMediaResult { 150 | revision: string, // the revision id of the page 151 | tid: string, 152 | items: Array // contains an array of media results 153 | } 154 | 155 | interface mediaResult { 156 | title: string, // title of image 157 | section_id: number, // section where image is present 158 | type: string, // type of media - can be image, video, or audio 159 | caption?: { // will be undefined for the infobox images 160 | html: string, // the html of the caption 161 | text: string // the text you probably want to use 162 | }, 163 | showInGallery: boolean, 164 | srcset: Array // this contains the url for the media 165 | } 166 | 167 | interface srcResult { 168 | src: string, // the url for the media 169 | scale: string 170 | } 171 | ``` 172 | 173 | ### eventResult 174 | 175 | The result for the `onThisDay` function call. 176 | ```js 177 | interface eventResult { 178 | births?: [ 179 | { 180 | text: string, 181 | pages: Array, 182 | year?: number 183 | } 184 | ], 185 | deaths?: [ 186 | { 187 | text: string, 188 | pages: Array, 189 | year?: number 190 | } 191 | ], 192 | events?: [ 193 | { 194 | text: string, 195 | pages: Array, 196 | year?: number 197 | } 198 | ], 199 | holidays?: [ 200 | { 201 | text: string, 202 | pages: Array 203 | } 204 | ], 205 | selected?: [ 206 | { 207 | text: string, 208 | pages: Array, 209 | year?: number 210 | } 211 | ] 212 | } 213 | ``` 214 | 215 | ### fcResult 216 | 217 | The result for the `featuredContent` function call. 218 | ```js 219 | interface fcResult { 220 | tfa: wikiSummary; 221 | mostread: { 222 | date: string; 223 | articles: Array 224 | }; 225 | image: { 226 | title: string; 227 | thumbnail: { 228 | source: string; 229 | width: number; 230 | height: number; 231 | }; 232 | image: { 233 | source: string; 234 | width: number; 235 | height: number; 236 | }; 237 | file_page: string; 238 | artist: Artist; 239 | credit: htmlText; 240 | license: { 241 | type: string; 242 | code: string; 243 | }; 244 | description: Description; 245 | wb_entity_id: string; 246 | structured: { 247 | captions: { 248 | [key: string]: string; 249 | } 250 | }; 251 | }; 252 | news: [ 253 | { 254 | links: Array; 255 | story: string; 256 | } 257 | ]; 258 | onthisday: [ 259 | { 260 | text: string; 261 | pages: Array; 262 | year: number; 263 | } 264 | ] 265 | } 266 | ``` 267 | 268 | ### relatedResult 269 | 270 | The related result. 271 | ```js 272 | interface relatedResult { 273 | pages: Array 274 | } 275 | ``` 276 | 277 | ### titleItem 278 | 279 | The title item object for the page 280 | ```js 281 | interface titleItem { 282 | title: string, 283 | page_id: number, 284 | rev: number, 285 | tid: number, 286 | namespace: number, 287 | user_id: number, 288 | user_text: string, 289 | timestamp: string, 290 | comment: string, 291 | tags: Array, 292 | restrictions: Array, 293 | page_language: string, 294 | redirect: boolean 295 | } 296 | 297 | // The result for the random titles query 298 | interface title { 299 | items: Array 300 | } 301 | ``` 302 | 303 | ### mobileSections 304 | 305 | The mobile sections for the page 306 | 307 | ```js 308 | interface mobileSections { 309 | lead: { 310 | ns: number, 311 | id: number, 312 | revision: string, 313 | lastmodified: string, 314 | lastmodifier: { 315 | user: string, 316 | gender: string 317 | }, 318 | displaytitle: string, 319 | normalizedtitle: string, 320 | wikibase_item: string, 321 | description: string, 322 | description_source: string, 323 | protection: Record, 324 | editable: boolean, 325 | languagecount: number, 326 | image: { 327 | file: string, 328 | urls: { 329 | 320: string, 330 | 640: string, 331 | 800: string, 332 | 1024: string 333 | } 334 | }, 335 | issues: Array, 336 | geo?: { 337 | latitude: string, 338 | longitude: string 339 | } 340 | sections: Array
341 | }, 342 | remaining: { 343 | sections: Array
344 | } 345 | } 346 | 347 | interface section { 348 | id: number, 349 | text: string, 350 | toclevel: number, 351 | line: string, 352 | anchor: string 353 | } 354 | ``` -------------------------------------------------------------------------------- /docs/wiki.md: -------------------------------------------------------------------------------- 1 | # Wiki 2 | 3 | ## Highlights 4 | 5 | - [Functions](#functions) 6 | - [search()](#search) 7 | - [page()](#page) 8 | - [geoSearch()](#geoSearch) 9 | - [onThisDay()](#onThisDay) 10 | - [featuredContent()](#featuredContent) 11 | - [languages()](#languages) 12 | - [setLang()](#setLang) 13 | - [setUserAgent()](#setUserAgent) 14 | - [suggest()](#suggest) 15 | - [random()](#random) 16 | - [citation()](#citation) 17 | - [Page Methods](#page-methods) 18 | 19 | ## Functions 20 | 21 | ### search() 22 | 23 | Returns the search result for the input string as [wikiSearchResult][2] object. Accepts two inputs, the first the query string and the second a [searchOptions][1] object. 24 | 25 | ```js 26 | search = async (query: string, searchOptions?: searchOptions): Promise 27 | ``` 28 | - @param query - The string you want to search for. This is required. 29 | - @param [searchOptions][1] - the options for the page 30 | - @result[wikiSearchResult][2] - the summary object for the wiki page 31 | 32 | ```js 33 | //example 34 | const searchResults = await wiki.search('Batma', {suggestion: true, limit: 10}); 35 | console.log(searchResults.results) // The search results 36 | console.log(searchResults.suggestion) // Suggestion string if suggestion set to true, null otherwise. 37 | ``` 38 | 39 | ### page() 40 | 41 | The main page method for the wiki. The title param can be either a string or a pageId. Returns a [Page][4] object which can be used to call any of the page methods. Use the `preload` and `fields` object present in [pageOptions][3] if you want to load multiple attributes of a page together. 42 | 43 | ```js 44 | page = async (title: string, pageOptions?: pageOptions): Promise 45 | ``` 46 | - @param title - The title or pageId of the page. This is required. 47 | - @param [pageOptions][3] - the options for the page 48 | - @result[Page][4] - the summary object for the wiki page 49 | 50 | ```js 51 | //example 52 | const page = await wiki.page('Batman', {autoSuggest: true, preload:true, fields:["summary", "html"]}); 53 | const summary = await page.summary(); // Does not call API, returns summary immediately as it is preloaded 54 | ``` 55 | ### onThisDay() 56 | 57 | Returns the events that happened on a day depending on input `type`, `month`, `day` arguments. Type can be any one of the ones defined in [eventOptions][23]. By default, it will return all events that happened on the current day. All options are documented in the [eventOptions][23] object. Returns a array of [eventResult][24] object which internally has arrays of [wikiSummary][22]. 58 | 59 | ```js 60 | onThisDay = async ({type: string, month: string, day: string}): Promise 61 | ``` 62 | - @param type - Any one of the valid event types. By default, `all`. 63 | - @param month - The month to search for. Takes current month by default. 64 | - @param day - The day to search for. Takes current day by default. 65 | - @result[eventResult][24] - a eventResult object. 66 | 67 | ```js 68 | //example 69 | const events = await wiki.onThisDay(); 70 | const deaths = await wiki.onThisDay({type:'deaths', month:'2', day:'28'}); 71 | console.log(events); // returns all the events which happened today 72 | console.log(deaths); // returns all deaths which happened on Feb 28 73 | ``` 74 | 75 | ### featuredContent() 76 | 77 | Returns featured content of a given day, depending on input `year`, `month`, `day` arguments. By default, it will return featured content of the current day. Returns a array of [fcResult][27] object. 78 | 79 | ```js 80 | featuredContent = async ({year: string, month: string, day: string}): Promise 81 | ``` 82 | - @param year - The year to search for. Takes current year by default. 83 | - @param month - The month to search for. Takes current month by default. 84 | - @param day - The day to search for. Takes current day by default. 85 | - @result [fcResult][27] - a fcResult object. 86 | 87 | ```js 88 | //example 89 | const content = await wiki.featuredContent(); 90 | const contentNewYear2020 = await wiki.featuredContent({year:'2020', month:'01', day:'01'}); 91 | const contentNewYear = await wiki.featuredContent({month:'01', day:'01'}); 92 | console.log(content); // returns featured content from today 93 | console.log(contentNewYear2020); // returns featured content from 2020-01-01 94 | console.log(contentNewYear); // returns featured content from 01-01 of this year 95 | ``` 96 | 97 | ### geoSearch() 98 | 99 | Searches for a page based on input `latitude`, `longitude` coordinates. Optionally takes a `limit` and `radius`(the search radius in meters) parameter in the [geoOptions][5] object. Returns an array of [geoSearchResult][6] object. 100 | 101 | ```js 102 | geoSearch = async (latitude: number, longitude: number, geoOptions?: geoOptions): Promise> 103 | ``` 104 | - @param latitude - The latitude to search for. This is required. 105 | - @param longitude - The longitude to search for. This is required. 106 | - @param [geoOptions][5] - the options for the geo search. 107 | - @result[geoSearchResult][6] - an array of geoSearchResult object. 108 | 109 | ```js 110 | //example 111 | const geoResult = await wiki.geoSearch(2.088, 4.023, { radius: 5000, limit: 20 }); 112 | console.log(geoResult[0]); // the closest page to given coordinates 113 | ``` 114 | 115 | ### languages() 116 | 117 | The array of languages available in wikipedia. Mainly meant for use before [setLang](#setLang) to check if the language is available before actually setting it. Returns an array of [languageResult][7] object. 118 | 119 | ```js 120 | languages = async (): Promise> 121 | ``` 122 | - @result[languageResult][6] - an array of languageResult object. 123 | 124 | ```js 125 | //example 126 | const languages = await wiki.languages(); 127 | ``` 128 | 129 | ### setLang() 130 | 131 | Uses the input language code to set language for the wikimedia and the REST APIs. This is useful in case you want to switch languages while using wikipedia. 132 | 133 | ```js 134 | setLang = (language: string): string 135 | ``` 136 | - @param language - The language code for the wiki. 137 | - @result Returns the new wikimedia url as string. 138 | 139 | ```js 140 | //example 141 | const changedLang = await wiki.setLang('fr'); // sets language to french 142 | ``` 143 | 144 | ### setUserAgent() 145 | 146 | Changes the user agent used in the request to wikipedia from 'wikipedia (https://github.com/dopecodez/Wikipedia/)' to the string passed. 147 | As per the wikipedia docs - Set a unique userAgent header that allows us to contact you quickly. Email addresses or URLs of contact pages work well. Check https://meta.wikimedia.org/wiki/User-Agent_policy for more info. 148 | 149 | ```js 150 | setUserAgent = (userAgent: string) 151 | ``` 152 | - @param language - The language code for the wiki. 153 | - @result null 154 | 155 | ```js 156 | //example 157 | await wiki.setUserAgent('Govind (gvind4@gmail.com)'); // sets userAgent to this value 158 | ``` 159 | 160 | ### suggest() 161 | 162 | Returns the wiki suggestion for a given query string. This method returns null if no suggestion was returned. 163 | 164 | ```js 165 | suggest = async (query: string): Promise 166 | ``` 167 | - @param query - The query string. 168 | - @result the suggestion or null if no suggestion present. 169 | 170 | ```js 171 | //example 172 | const suggestion = await wiki.suggest('Batma'); 173 | console.log(suggestion); //Returns 'Batman' 174 | ``` 175 | 176 | ### random() 177 | 178 | Returns a random wiki page in any of the available formats. Formats can be `summary`, `title`, `related`, `mobile-sections` or `mobile-sections-lead`. Defaults to summary. 179 | 180 | ```js 181 | random = async (format?: randomFormats): Promise 182 | ``` 183 | - @param format - the format for the random page 184 | - @result the random page in requested format 185 | 186 | ```js 187 | //example 188 | const randomSummary = await wiki.random(); 189 | console.log(random); //Returns wikiSummary of a random pageOption 190 | const randomMobileSections = await wiki.random("mobile-sections"); 191 | console.log(randomMobileSections); // Returns random mobile sections for a page 192 | ``` 193 | ### citation 194 | 195 | Generates citation data given a URL, DOI, PMID, ISBN, or PMCID. Defaults to mediawiki format. Format can be 'mediawiki', 'mediawiki-basefields', 'zotero', 'bibtex', 'wikibase'. 196 | 197 | ```js 198 | citation = async (query: string, format?: citationFormat, language?: string): Promise 199 | ``` 200 | - @param query - url or query string 201 | - @param format - the format of the citation result 202 | - @param language - if you want lanuage enabled results 203 | - @returns Returns citation data 204 | 205 | ```js 206 | //example 207 | const citation = await wiki.citation("batman"); 208 | console.log(random); //Returns citations in an array 209 | const citationsWiki = await wiki.citation("test", "mediawiki", "fr") ; 210 | console.log(citationsWiki); // Returns citations in mediawiki format 211 | ``` 212 | 213 | #### autocompletions() 214 | 215 | Returns the autocompletions of a given title 216 | 217 | ```js 218 | autocompletions = async (autocompletionOptions?: autocompletionOptions): Promise> 219 | ``` 220 | - @param [autocompletionOptions][28] - the options for the autocompletions 221 | 222 | ```js 223 | //example 224 | const autocompletions = await wiki.autocompletions({limit: 20}); 225 | ``` 226 | 227 | ### Page Methods 228 | 229 | All the methods defined in the [Page][4] documentation can be called directly from the wiki object as well. This includes [summary()][8], [images()][9], [intro()][10], [html()][11], [related()][12], [content()][13], [categories()][14], [links()][15], [references()][16], [coordinates()][17], [langLinks()][18], [infobox()][19], [mobileHtml()][25], [pdf()][26] and [tables()][20]. 230 | 231 | **Read up [here][21] to understand when you should use these methods directly and when you should use the page methods**. 232 | Also, note that if called directly from the wiki object, you can use the `autoSuggest` option present in the [pageOptions][3] object which will be ignored if called through page. 233 | ```js 234 | //example 235 | const summary = await wiki.summary('Batma', {autoSuggest:true}); 236 | console.log(summary); //Returns summary for 'Batman' 237 | const html = await wiki.summary('David Attenborough'); 238 | console.log(html); //Returns html for the environmentalist 239 | ``` 240 | 241 | [1]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md#searchOptions 242 | [2]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#wikiSearchResult 243 | [3]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md#pageOptions 244 | [4]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md 245 | [5]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md#geoOptions 246 | [6]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#geoSearchResult 247 | [7]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#languageResult 248 | [8]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#summary 249 | [9]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#images 250 | [10]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#intro 251 | [11]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#html 252 | [12]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#related 253 | [13]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#content 254 | [14]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#categories 255 | [15]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#links 256 | [16]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#references 257 | [17]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#coordinates 258 | [18]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#langLinks 259 | [19]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#infobox 260 | [20]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#tables 261 | [21]: https://github.com/dopecodez/wikipedia/blob/master/docs/USAGE.md#when-to-use-page 262 | [22]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#wikiSummary 263 | [23]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md#eventOptions 264 | [24]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#eventResult 265 | [25]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#mobileHtml 266 | [26]: https://github.com/dopecodez/wikipedia/blob/master/docs/PAGE.md#pdf 267 | [27]: https://github.com/dopecodez/wikipedia/blob/master/docs/resultTypes.md#fcResult 268 | [28]: https://github.com/dopecodez/wikipedia/blob/master/docs/optionTypes.md#autocompletionOptions 269 | 270 | 271 | 272 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | testEnvironment: 'node', 5 | transform: { 6 | '^.+\\.tsx?$': ['ts-jest', { 7 | isolatedModules: true, 8 | }] 9 | }, 10 | testRegex: './test/.+\\.test\\.ts$', 11 | collectCoverage: false, 12 | collectCoverageFrom: ['source/*.{js,jsx,ts,tsx}'], 13 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 14 | coverageReporters: ['text-summary', 'lcov'], 15 | }; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wikipedia", 3 | "version": "2.1.2", 4 | "description": "A JavaScript wrapper for the wikipedia apis", 5 | "main": "dist", 6 | "engines": { 7 | "node": ">=10" 8 | }, 9 | "scripts": { 10 | "prepublishOnly": "tsc --build --clean && tsc", 11 | "test": "jest --coverage", 12 | "build": "yarn lint && tsc --build --clean && tsc", 13 | "prepare": "yarn build", 14 | "lint": "eslint source/*.ts", 15 | "release": "np" 16 | }, 17 | "author": "Govind S", 18 | "license": "MIT", 19 | "homepage": "https://github.com/dopecodez/wikipedia#README", 20 | "repository": { 21 | "type": "git", 22 | "url": "git://github.com/dopecodez/wikipedia.git" 23 | }, 24 | "devDependencies": { 25 | "@types/infobox-parser": "^3.3.1", 26 | "@types/jest": "^29.2.4", 27 | "@types/node": "^18.11.17", 28 | "@typescript-eslint/eslint-plugin": "^5.47.0", 29 | "@typescript-eslint/parser": "^5.47.0", 30 | "eslint": "^8.30.0", 31 | "eslint-plugin-tsdoc": "^0.2.17", 32 | "jest": "^29.3.1", 33 | "ts-jest": "^29.0.3", 34 | "typescript": "^4.9.4" 35 | }, 36 | "files": [ 37 | "dist" 38 | ], 39 | "types": "dist/", 40 | "dependencies": { 41 | "axios": "^1.4.0", 42 | "infobox-parser": "^3.6.2" 43 | }, 44 | "keywords": [ 45 | "wiki", 46 | "wikipedia", 47 | "wikimedia", 48 | "encyclopedia", 49 | "jest", 50 | "node-wiki", 51 | "wiki-node" 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /source/errors.ts: -------------------------------------------------------------------------------- 1 | export class wikiError extends Error { 2 | code?: string; 3 | constructor(message: string, code?: string) { 4 | super(message); 5 | this.name = 'wikiError'; 6 | this.code = code; 7 | } 8 | } 9 | 10 | export class searchError extends wikiError { 11 | constructor(message: string) { 12 | super(message); 13 | this.name = 'searchError'; 14 | } 15 | } 16 | 17 | export class autocompletionsError extends wikiError { 18 | constructor(message: string) { 19 | super(message); 20 | this.name = 'autocompletionsError'; 21 | } 22 | } 23 | 24 | 25 | export class pageError extends wikiError { 26 | constructor(message: string) { 27 | super(message); 28 | this.name = 'pageError'; 29 | } 30 | } 31 | 32 | export class summaryError extends wikiError { 33 | constructor(message: string) { 34 | super(message); 35 | this.name = 'summaryError'; 36 | } 37 | } 38 | 39 | export class imageError extends wikiError { 40 | constructor(message: string) { 41 | super(message); 42 | this.name = 'imageError'; 43 | } 44 | } 45 | 46 | export class htmlError extends wikiError { 47 | constructor(message: string) { 48 | super(message); 49 | this.name = 'htmlError'; 50 | } 51 | } 52 | 53 | export class contentError extends wikiError { 54 | constructor(message: string) { 55 | super(message); 56 | this.name = 'contentError'; 57 | } 58 | } 59 | 60 | export class categoriesError extends wikiError { 61 | constructor(message: string) { 62 | super(message); 63 | this.name = 'categoriesError'; 64 | } 65 | } 66 | 67 | export class linksError extends wikiError { 68 | constructor(message: string) { 69 | super(message); 70 | this.name = 'linksError'; 71 | } 72 | } 73 | 74 | export class geoSearchError extends wikiError { 75 | constructor(message: string) { 76 | super(message); 77 | this.name = 'geoSearchError'; 78 | } 79 | } 80 | 81 | export class coordinatesError extends wikiError { 82 | constructor(message: string) { 83 | super(message); 84 | this.name = 'coordinatesError'; 85 | } 86 | } 87 | 88 | export class infoboxError extends wikiError { 89 | constructor(message: string) { 90 | super(message); 91 | this.name = 'infoboxError'; 92 | } 93 | } 94 | 95 | export class preloadError extends wikiError { 96 | constructor(message: string) { 97 | super(message); 98 | this.name = 'preloadError'; 99 | } 100 | } 101 | 102 | export class introError extends wikiError { 103 | constructor(message: string) { 104 | super(message); 105 | this.name = 'introError'; 106 | } 107 | } 108 | 109 | export class relatedError extends wikiError { 110 | constructor(message: string) { 111 | super(message); 112 | this.name = 'relatedError'; 113 | } 114 | } 115 | 116 | export class mediaError extends wikiError { 117 | constructor(message: string) { 118 | super(message); 119 | this.name = 'mediaError'; 120 | } 121 | } 122 | 123 | export class eventsError extends wikiError { 124 | constructor(message: string) { 125 | super(message); 126 | this.name = 'eventsError'; 127 | } 128 | } 129 | 130 | export class fcError extends wikiError { 131 | constructor(message: string) { 132 | super(message); 133 | this.name = 'featuredContentError'; 134 | } 135 | } 136 | 137 | export class pdfError extends wikiError { 138 | constructor(message: string) { 139 | super(message); 140 | this.name = 'pdfError'; 141 | } 142 | } 143 | 144 | export class citationError extends wikiError { 145 | constructor(message: string) { 146 | super(message); 147 | this.name = 'citationError'; 148 | } 149 | } -------------------------------------------------------------------------------- /source/index.ts: -------------------------------------------------------------------------------- 1 | import request, { makeRestRequest, setAPIUrl, setUserAgent } from './request' 2 | import { pageOptions, searchOptions, geoOptions, listOptions, eventOptions, fcOptions, randomFormats, pdfOptions, citationFormat, autocompletionOptions } from './optionTypes'; 3 | import Page, { 4 | intro, images, html, content, categories, links, coordinates, langLinks, 5 | references, infobox, tables, summary, related, media, mobileHtml, pdf, citation 6 | } from './page'; 7 | import { coordinatesResult, eventResult, featuredContentResult, geoSearchResult, imageResult, langLinksResult, languageResult, 8 | mobileSections, relatedResult, 9 | title, wikiMediaResult, wikiSearchResult, wikiSummary, notFound } from './resultTypes'; 10 | import { 11 | categoriesError, 12 | contentError, coordinatesError, eventsError, fcError, geoSearchError, htmlError, imageError, infoboxError, 13 | introError, linksError, mediaError, pageError, relatedError, searchError, summaryError, wikiError, 14 | pdfError, 15 | citationError, 16 | autocompletionsError 17 | } from './errors'; 18 | import { MSGS } from './messages'; 19 | import { getCurrentDay, getCurrentMonth, getCurrentYear, setPageId, setPageIdOrTitleParam, setTitleForPage } from './utils'; 20 | 21 | /** 22 | * The default wiki export 23 | * 24 | * @remarks 25 | * Internally calls wiki.page 26 | * 27 | */ 28 | const wiki = async (title: string, pageOptions?: pageOptions): Promise => { 29 | return wiki.page(title, pageOptions); 30 | } 31 | 32 | /** 33 | * Returns the search results for a given query 34 | * 35 | * @remarks 36 | * Limits results by default to 10 37 | * 38 | * @param query - The string to search for 39 | * @param searchOptions - The number of results and if suggestion needed {@link searchOptions | searchOptions } 40 | * @returns an array of {@link wikiSearchResult | wikiSearchResult } 41 | */ 42 | wiki.search = async (query: string, searchOptions?: searchOptions): Promise => { 43 | try { 44 | const searchParams: any = { 45 | 'list': 'search', 46 | 'srprop': '', 47 | 'srlimit': searchOptions?.limit || 10, 48 | 'srsearch': query 49 | } 50 | searchOptions?.suggestion ? searchParams['srinfo'] = 'suggestion' : null; 51 | const response = await request(searchParams); 52 | const result: wikiSearchResult = { 53 | results: response.query.search, 54 | suggestion: response.query.searchinfo ? response.query.searchinfo.suggestion : null 55 | } 56 | return result; 57 | } catch (error) { 58 | throw new searchError(error); 59 | } 60 | } 61 | 62 | /** 63 | * Returns the page for a given title or string 64 | * 65 | * @remarks 66 | * Call this method to get the basic info for page and also to preload any params you might use in future 67 | * 68 | * @param title - The title or page Id of the page 69 | * @param pageOptions - Whether to redirect, autoSuggest or preload any fields {@link pageOptions | pageOptions } 70 | * @returns The intro string 71 | */ 72 | wiki.page = async (title: string, pageOptions?: pageOptions): Promise => { 73 | try { 74 | if (pageOptions?.autoSuggest) { 75 | title = await setTitleForPage(title); 76 | } 77 | let pageParams: any = { 78 | prop: 'info|pageprops', 79 | inprop: 'url', 80 | ppprop: 'disambiguation', 81 | } 82 | pageParams = setPageIdOrTitleParam(pageParams, title); 83 | const response = await request(pageParams); 84 | let pageInfo = response.query.pages; 85 | const pageId = setPageId(pageParams, response); 86 | pageInfo = pageInfo[pageId]; 87 | if (pageInfo.missing == '') { 88 | throw new pageError(`${MSGS.PAGE_NOT_EXIST}${title}`) 89 | } 90 | const page = new Page(pageInfo); 91 | if (pageOptions?.preload) { 92 | if (!pageOptions?.fields) { 93 | pageOptions.fields = ['summary', 'images']; 94 | } 95 | for (const field of pageOptions.fields) { 96 | await page.runMethod(field); 97 | } 98 | } 99 | return page; 100 | } catch (error) { 101 | throw new pageError(error); 102 | } 103 | } 104 | 105 | /** 106 | * Returns the intro present in a wiki page 107 | * 108 | * @remarks 109 | * Called in page object and also through wiki default object 110 | * 111 | * @param title - The title or page Id of the page 112 | * @param pageOptions - Whether to redirect in case of 302 113 | * @returns The intro string 114 | */ 115 | wiki.intro = async (title: string, pageOptions?: pageOptions): Promise => { 116 | try { 117 | if (pageOptions?.autoSuggest) { 118 | title = await setTitleForPage(title); 119 | } 120 | const result = await intro(title, pageOptions?.redirect); 121 | return result; 122 | } catch (error) { 123 | throw new introError(error); 124 | } 125 | } 126 | 127 | /** 128 | * Returns the images present in a wiki page 129 | * 130 | * @remarks 131 | * Called in page object and also through wiki default object 132 | * 133 | * @param title - The title or page Id of the page 134 | * @param listOptions - {@link listOptions | listOptions } 135 | * @returns an array of imageResult {@link imageResult | imageResult } 136 | */ 137 | wiki.images = async (title: string, listOptions?: listOptions): Promise> => { 138 | try { 139 | if (listOptions?.autoSuggest) { 140 | title = await setTitleForPage(title); 141 | } 142 | const result = await images(title, listOptions); 143 | return result; 144 | } catch (error) { 145 | throw new imageError(error); 146 | } 147 | } 148 | 149 | /** 150 | * Returns the summary of the page 151 | * 152 | * @remarks 153 | * Called in page object and also through wiki default object 154 | * 155 | * @param title - The title or page Id of the page 156 | * @param pageOptions - Whether to redirect in case of 302 157 | * @returns The summary of the page as {@link wikiSummary | wikiSummary} 158 | */ 159 | wiki.summary = async (title: string, pageOptions?: pageOptions): Promise => { 160 | try { 161 | if (pageOptions?.autoSuggest) { 162 | title = await setTitleForPage(title); 163 | } 164 | const result = await summary(title, pageOptions?.redirect); 165 | return result; 166 | } catch (error) { 167 | throw new summaryError(error); 168 | } 169 | } 170 | 171 | /** 172 | * Returns the html content of a page 173 | * 174 | * @remarks 175 | * Called in page object and also through wiki default object 176 | * 177 | * @param title - The title or page Id of the page 178 | * @param pageOptions - Whether to redirect in case of 302 179 | * @returns The html content as string 180 | * 181 | * @beta 182 | */ 183 | wiki.html = async (title: string, pageOptions?: pageOptions): Promise => { 184 | try { 185 | if (pageOptions?.autoSuggest) { 186 | title = await setTitleForPage(title); 187 | } 188 | const result = await html(title, pageOptions?.redirect); 189 | return result; 190 | } catch (error) { 191 | throw new htmlError(error); 192 | } 193 | } 194 | 195 | /** 196 | * Returns the plain text content of a page 197 | * 198 | * @remarks 199 | * Called in page object and also through wiki default object 200 | * 201 | * @param title - The title or page Id of the page 202 | * @param pageOptions - Whether to redirect in case of 302 203 | * @returns The plain text as string and the parent and revision ids 204 | */ 205 | wiki.content = async (title: string, pageOptions?: pageOptions): Promise => { 206 | try { 207 | if (pageOptions?.autoSuggest) { 208 | title = await setTitleForPage(title); 209 | } 210 | const response = await content(title, pageOptions?.redirect); 211 | return response.result; 212 | } catch (error) { 213 | throw new contentError(error); 214 | } 215 | } 216 | 217 | /** 218 | * Returns the cetegories present in page 219 | * 220 | * @remarks 221 | * Called in page object and also through wiki default object 222 | * 223 | * @param title - The title or page Id of the page 224 | * @param listOptions - {@link listOptions | listOptions } 225 | * @returns The categories as an array of string 226 | */ 227 | wiki.categories = async (title: string, listOptions?: listOptions): Promise> => { 228 | try { 229 | if (listOptions?.autoSuggest) { 230 | title = await setTitleForPage(title); 231 | } 232 | const response = await categories(title, listOptions); 233 | return response; 234 | } catch (error) { 235 | throw new categoriesError(error); 236 | } 237 | } 238 | 239 | /** 240 | * Returns summaries for 20 pages related to the given page. Summaries include page title, namespace 241 | * and id along with short text description of the page and a thumbnail. 242 | * 243 | * @remarks 244 | * Called in page object and also through index 245 | * 246 | * @param title - The title or page Id of the page 247 | * @param pageOptions - Whether to redirect in case of 302 248 | * @returns The related pages and summary as an array of {@link wikiSummary | wikiSummary} 249 | * 250 | * @experimental 251 | */ 252 | wiki.related = async (title: string, pageOptions?: pageOptions): Promise => { 253 | try { 254 | if (pageOptions?.autoSuggest) { 255 | title = await setTitleForPage(title); 256 | } 257 | const response = await related(title, pageOptions?.redirect); 258 | return response; 259 | } catch (error) { 260 | throw new relatedError(error); 261 | } 262 | } 263 | 264 | /** 265 | * Gets the list of media items (images, audio, and video) in the 266 | * order in which they appear on a given wiki page. 267 | * 268 | * @remarks 269 | * Called in page object and also through index 270 | * 271 | * @param title - The title or page Id of the page 272 | * @param redirect - Whether to redirect in case of 302 273 | * @returns The related pages and summary as an array of {@link wikiMediaResult | wikiMediaResult} 274 | * 275 | * @experimental 276 | */ 277 | wiki.media = async (title: string, pageOptions?: pageOptions): Promise => { 278 | try { 279 | if (pageOptions?.autoSuggest) { 280 | title = await setTitleForPage(title); 281 | } 282 | const response = await media(title, pageOptions?.redirect); 283 | return response; 284 | } catch (error) { 285 | throw new mediaError(error); 286 | } 287 | } 288 | 289 | /** 290 | * Returns the links present in page 291 | * 292 | * @remarks 293 | * Called in page object and also through wiki default object 294 | * 295 | * @param title - The title or page Id of the page 296 | * @param listOptions - {@link listOptions | listOptions } 297 | * @returns The links as an array of string 298 | */ 299 | wiki.links = async (title: string, listOptions?: listOptions): Promise> => { 300 | try { 301 | if (listOptions?.autoSuggest) { 302 | title = await setTitleForPage(title); 303 | } 304 | const response = await links(title, listOptions); 305 | return response; 306 | } catch (error) { 307 | throw new linksError(error); 308 | } 309 | } 310 | 311 | /** 312 | * Returns the references of external links present in page 313 | * 314 | * @remarks 315 | * Called in page object and also through wiki default object 316 | * 317 | * @param title - The title or page Id of the page 318 | * @param listOptions - {@link listOptions | listOptions } 319 | * @returns The references as an array of string 320 | */ 321 | wiki.references = async (title: string, listOptions?: listOptions): Promise> => { 322 | try { 323 | if (listOptions?.autoSuggest) { 324 | title = await setTitleForPage(title); 325 | } 326 | const response = await references(title, listOptions); 327 | return response; 328 | } catch (error) { 329 | throw new linksError(error); 330 | } 331 | } 332 | 333 | /** 334 | * Returns the coordinates of a page 335 | * 336 | * @remarks 337 | * Called in page object and also through wiki default object 338 | * 339 | * @param title - The title or page Id of the page 340 | * @param pageOptions - Whether to redirect in case of 302 341 | * @returns The coordinates as {@link coordinatesResult | coordinatesResult} 342 | */ 343 | wiki.coordinates = async (title: string, pageOptions?: pageOptions): Promise => { 344 | try { 345 | if (pageOptions?.autoSuggest) { 346 | title = await setTitleForPage(title); 347 | } 348 | const response = await coordinates(title, pageOptions?.redirect); 349 | return response; 350 | } catch (error) { 351 | throw new coordinatesError(error); 352 | } 353 | } 354 | 355 | /** 356 | * Returns the language links present in the page 357 | * 358 | * @remarks 359 | * Called in page object and also through wiki default object 360 | * 361 | * @param title - The title or page Id of the page 362 | * @param listOptions - {@link listOptions | listOptions } 363 | * @returns The links as an array of {@link langLinksResult | langLinksResult } 364 | */ 365 | wiki.langLinks = async (title: string, listOptions?: listOptions): Promise> => { 366 | try { 367 | if (listOptions?.autoSuggest) { 368 | title = await setTitleForPage(title); 369 | } 370 | const response = await langLinks(title, listOptions); 371 | return response; 372 | } catch (error) { 373 | throw new linksError(error); 374 | } 375 | } 376 | 377 | /** 378 | * Returns the infobox content of page if present 379 | * 380 | * @remarks 381 | * Called in page object and also through wiki default object 382 | * 383 | * @param title - The title or page Id of the page 384 | * @param pageOptions - Whether to redirect in case of 302 385 | * @returns The info as JSON object 386 | */ 387 | wiki.infobox = async (title: string, pageOptions?: pageOptions): Promise => { 388 | try { 389 | if (pageOptions?.autoSuggest) { 390 | title = await setTitleForPage(title); 391 | } 392 | const response = await infobox(title, pageOptions?.redirect); 393 | return response; 394 | } catch (error) { 395 | throw new infoboxError(error); 396 | } 397 | } 398 | 399 | /** 400 | * Returns the table content of page if present 401 | * 402 | * @remarks 403 | * Called in page object and also through wiki default object 404 | * 405 | * @param title - The title or page Id of the page 406 | * @param pageOptions - Whether to redirect in case of 302 407 | * @returns The tables as arrays of JSON objects 408 | */ 409 | wiki.tables = async (title: string, pageOptions?: pageOptions): Promise> => { 410 | try { 411 | if (pageOptions?.autoSuggest) { 412 | title = await setTitleForPage(title); 413 | } 414 | const response = await tables(title, pageOptions?.redirect); 415 | return response; 416 | } catch (error) { 417 | throw new infoboxError(error); 418 | } 419 | } 420 | 421 | /** 422 | * Returns the languages available in wiki 423 | * 424 | * @remarks 425 | * Use this if you want to check if a lanuage exists before actually setting it 426 | * 427 | * @returns The languages an array of {@link languageResult | languageResult} 428 | */ 429 | wiki.languages = async (): Promise> => { 430 | try { 431 | const langParams = { 432 | 'meta': 'siteinfo', 433 | 'siprop': 'languages' 434 | } 435 | const response = await request(langParams); 436 | const languages = []; 437 | for (const lang of response.query.languages) { 438 | languages.push({ [lang.code]: lang['*'] }) 439 | } 440 | return languages; 441 | } catch (error) { 442 | throw new wikiError(error); 443 | } 444 | } 445 | 446 | /** 447 | * sets the languages to given string - verify your input using languages method 448 | * 449 | * @remarks 450 | * Use this to set your language for future api calls 451 | * 452 | * @returns The new api endpoint as string 453 | */ 454 | wiki.setLang = (language: string): string => { 455 | const apiUrl = setAPIUrl(language); 456 | return apiUrl; 457 | } 458 | 459 | /** 460 | * Returns the pages with coordinates near the geo search coordinates 461 | * 462 | * @remarks 463 | * Latitude and longitude should be valid values 464 | * 465 | * @param latitude - The latitude to search 466 | * @param longitude - The longitude to search 467 | * @param geoOptions - The number of results and the search radius {@link geoOptions | geoOptions} 468 | * @returns The results as an array of {@link geoSearchResult | geoSearchResult} 469 | */ 470 | wiki.geoSearch = async (latitude: number, longitude: number, geoOptions?: geoOptions): Promise> => { 471 | try { 472 | const geoSearchParams: any = { 473 | 'list': 'geosearch', 474 | 'gsradius': geoOptions?.radius || 1000, 475 | 'gscoord': `${latitude}|${longitude}`, 476 | 'gslimit': geoOptions?.limit || 10, 477 | 'gsprop': 'type' 478 | } 479 | const results = await request(geoSearchParams); 480 | const searchPages = results.query.geosearch; 481 | return searchPages; 482 | } catch (error) { 483 | throw new geoSearchError(error); 484 | } 485 | } 486 | 487 | /** 488 | * Returns the suggestion for a given query 489 | * 490 | * @remarks 491 | * Use this if you want your user to approve the suggestion before using it 492 | * 493 | * @param query - The string to query 494 | * @returns Returns a string or null based on if suggestion is present or not 495 | */ 496 | wiki.suggest = async (query: string): Promise => { 497 | try { 498 | const suggestParams = { 499 | 'list': 'search', 500 | 'srinfo': 'suggestion', 501 | 'srprop': '', 502 | 'srsearch': query 503 | } 504 | const result = await request(suggestParams); 505 | return result.query?.searchinfo?.suggestion ? result.query?.searchinfo?.suggestion : null; 506 | } catch (error) { 507 | throw new searchError(error); 508 | } 509 | } 510 | 511 | /** 512 | * Returns the events for a given day 513 | * 514 | * @remarks 515 | * The api returns the events that happened on a particular month and day 516 | * 517 | * @param eventOptions - the event types, and the month and day {@link eventOptions | eventOptions} 518 | * @returns Returns the results as array of {@link eventResult | eventResult} 519 | */ 520 | wiki.onThisDay = async (eventOptions: eventOptions = {}): Promise => { 521 | try { 522 | const type = eventOptions.type || 'all'; 523 | const mm = (eventOptions.month || getCurrentMonth()).toString().padStart(2, "0") 524 | const dd = (eventOptions.day || getCurrentDay()).toString().padStart(2, "0") 525 | const path = `feed/onthisday/${type}/${mm}/${dd}`; 526 | const result = await makeRestRequest(path, true); 527 | return result; 528 | } catch (error) { 529 | throw new eventsError(error); 530 | } 531 | } 532 | 533 | /** 534 | * Returns featured content for a given day 535 | * 536 | * @remarks 537 | * The api returns content featured at a particular date 538 | * 539 | * @param fcOptions - the year/month/day of featured content by {@link fcOptions | eventOptions} 540 | * @returns Returns the results as array of {@link fcResult | fcResult} 541 | */ 542 | wiki.featuredContent = async (fcOptions: fcOptions = {}): Promise => { 543 | try { 544 | const yyyy = (fcOptions.year || getCurrentYear()).toString() 545 | const mm = (fcOptions.month || getCurrentMonth()).toString().padStart(2, "0") 546 | const dd = (fcOptions.day || getCurrentDay()).toString().padStart(2, "0") 547 | const path = `feed/featured/${yyyy}/${mm}/${dd}`; 548 | const result = await makeRestRequest(path, true); 549 | return result; 550 | } catch (error) { 551 | throw new fcError(error); 552 | } 553 | } 554 | 555 | /** 556 | * Returns a random page 557 | * 558 | * @param format - The desired return format 559 | * @returns Returns content from a random page 560 | */ 561 | wiki.random = async (format?: randomFormats): Promise => { 562 | try { 563 | if(!format){ 564 | format = 'summary'; 565 | } 566 | const path = `page/random/${format}`; 567 | const result = await makeRestRequest(path); 568 | return result; 569 | } catch (error) { 570 | throw new wikiError(error); 571 | } 572 | } 573 | 574 | /** 575 | * Returns mobile-optimised HTML of a page 576 | * 577 | * @param title - The title of the page to query 578 | * @param pageOptions - Whether to redirect in case of 302 579 | * @returns Returns HTML string 580 | */ 581 | wiki.mobileHtml = async (title: string, pageOptions?: pageOptions): Promise => { 582 | try { 583 | if (pageOptions?.autoSuggest) { 584 | title = await setTitleForPage(title); 585 | } 586 | const result = await mobileHtml(title, pageOptions?.redirect); 587 | return result; 588 | } catch (error) { 589 | throw new htmlError(error); 590 | } 591 | } 592 | 593 | /** 594 | * Returns pdf of a given page 595 | * 596 | * @param title - The title of the page to query 597 | * @param pdfOptions - {@link pdfOptions | pdfOptions } 598 | * @returns Returns pdf format 599 | */ 600 | wiki.pdf = async (title: string, pdfOptions?: pdfOptions): Promise => { 601 | try { 602 | if (pdfOptions?.autoSuggest) { 603 | title = await setTitleForPage(title); 604 | } 605 | const result = await pdf(title, pdfOptions); 606 | return result; 607 | } catch (error) { 608 | throw new pdfError(error); 609 | } 610 | } 611 | 612 | /** 613 | * Returns citation of a given page, or query string 614 | * 615 | * @param format - the format of the citation result 616 | * @param query - url or query string 617 | * @param language - if you want lanuage enabled results 618 | * @returns Returns citation data 619 | */ 620 | wiki.citation = async (query: string, format?: citationFormat, language?: string): Promise => { 621 | try { 622 | const result = await citation(query, format, language); 623 | return result; 624 | } catch (error) { 625 | throw new citationError(error); 626 | } 627 | } 628 | 629 | /** 630 | * Returns the autocompletion results for a given query 631 | * 632 | * @remarks 633 | * Limits results by default to 10 634 | * 635 | * @param query - The string to search for 636 | * @param autocompletionOptions - The number of results {@link autocompletionOptions | autocompletionOptions } 637 | * @returns an array of string 638 | */ 639 | wiki.autocompletions = async (query: string, autocompletionOptions?: autocompletionOptions): Promise> => { 640 | try { 641 | const autocompletionsParams: any = { 642 | list: "search", 643 | limit: autocompletionOptions?.limit || 10, 644 | search: query, 645 | action: "opensearch", 646 | redirect: "return" 647 | }; 648 | 649 | const [, autocompletions] = await request(autocompletionsParams, false); 650 | 651 | return autocompletions; 652 | } catch (error) { 653 | throw new autocompletionsError(error); 654 | } 655 | } 656 | 657 | /** 658 | * Change the default user agent for wikipedia 659 | * 660 | * @param userAgent - The new custom userAgent 661 | */ 662 | wiki.setUserAgent = (userAgent: string) => { 663 | setUserAgent(userAgent); 664 | } 665 | 666 | export default wiki; 667 | // For CommonJS default export support 668 | module.exports = wiki; 669 | module.exports.default = wiki; 670 | 671 | export * from './errors'; 672 | export * from './resultTypes'; 673 | export * from './optionTypes'; 674 | export * from './page'; -------------------------------------------------------------------------------- /source/messages.ts: -------------------------------------------------------------------------------- 1 | export const MSGS = { 2 | PAGE_NOT_SUGGEST: 'No page with given title suggested : ', 3 | PAGE_NOT_EXIST: 'No page with given title exists : ', 4 | INFOBOX_NOT_EXIST: 'Info cannot be parsed for given page' 5 | } -------------------------------------------------------------------------------- /source/optionTypes.ts: -------------------------------------------------------------------------------- 1 | export interface searchOptions { 2 | limit?: number 3 | suggestion?: boolean 4 | } 5 | 6 | export interface autocompletionOptions { 7 | limit?: number 8 | } 9 | 10 | export interface pageOptions { 11 | autoSuggest?: boolean 12 | redirect?: boolean 13 | preload?: boolean 14 | fields?: Array 15 | } 16 | 17 | export interface listOptions { 18 | autoSuggest?: boolean 19 | redirect?: boolean 20 | limit?: number 21 | } 22 | 23 | export interface geoOptions { 24 | limit?: number 25 | radius?: number 26 | } 27 | 28 | export type pageFunctions = 29 | 'summary' | 'images' | 'intro' | 'html' | 'content' | 'categories' | 'links' | 'references' | 'coordinates' 30 | | 'langLinks' | 'infobox' | 'tables' | 'related' 31 | 32 | export interface eventOptions { 33 | type?: eventTypes, 34 | month?: string, 35 | day?: string 36 | } 37 | 38 | export interface fcOptions { 39 | year?: string, 40 | month?: string, 41 | day?: string 42 | } 43 | 44 | export type eventTypes = 45 | 'all' | 'selected' | 'births' | 'deaths' | 'events' | 'holidays' 46 | 47 | export type randomFormats = 48 | 'title' | 'summary' | 'related' | 'mobile-sections' | 'mobile-sections-lead' 49 | 50 | export type format = 51 | 'a4' | 'letter' | 'legal' 52 | 53 | export type pdfType = 54 | 'desktop' | 'mobile' 55 | 56 | export interface pdfOptions { 57 | autoSuggest?: boolean 58 | format?: format 59 | type?: pdfType 60 | } 61 | 62 | export type citationFormat = 63 | 'mediawiki' | 'mediawiki-basefields' | 'zotero' | 'bibtex' | 'wikibase' -------------------------------------------------------------------------------- /source/page.ts: -------------------------------------------------------------------------------- 1 | import { categoriesError, contentError, coordinatesError, htmlError, imageError, wikiError, pdfError, 2 | infoboxError, introError, linksError, mediaError, preloadError, relatedError, summaryError, citationError } from './errors'; 3 | import request, { makeRestRequest, returnRestUrl } from './request'; 4 | import { coordinatesResult, imageResult, langLinksResult, notFound, pageResult, relatedResult, wikiMediaResult, wikiSummary } from './resultTypes'; 5 | import { setPageId, setPageIdOrTitleParam } from './utils'; 6 | import { citationFormat, listOptions, pageOptions, pdfOptions } from './optionTypes'; 7 | import { MSGS } from './messages'; 8 | 9 | // eslint-disable-next-line @typescript-eslint/no-var-requires 10 | const infoboxParser = require('infobox-parser'); 11 | 12 | 13 | export class Page { 14 | pageid!: number; 15 | ns!: number; 16 | title!: string; 17 | contentmodel!: string; 18 | pagelanguage!: string; 19 | pagelanguagehtmlcode!: string; 20 | pagelanguagedir!: string; 21 | touched!: string; 22 | lastrevid!: number; 23 | length!: number; 24 | fullurl!: string; 25 | editurl!: string; 26 | canonicalurl!: string; 27 | revid!: number; 28 | parentid!: number; 29 | _summary!: wikiSummary; 30 | _images!: Array; 31 | _content!: string; 32 | _html!: string; 33 | _categories!: Array; 34 | _references!: Array; 35 | _links!: Array; 36 | _coordinates!: coordinatesResult; 37 | _langLinks!: Array; 38 | _infobox!: any; 39 | _tables!: Array; 40 | _intro!: string; 41 | _related!: relatedResult; 42 | _media!: wikiMediaResult; 43 | _mobileHtml!: string | notFound; 44 | constructor(response: pageResult) { 45 | this.pageid = response.pageid; 46 | this.ns = response.ns; 47 | this.title = response.title; 48 | this.contentmodel = response.contentmodel; 49 | this.pagelanguage = response.pagelanguage; 50 | this.pagelanguagedir = response.pagelanguagedir; 51 | this.touched = response.touched; 52 | this.lastrevid = response.lastrevid; 53 | this.length = response.length; 54 | this.fullurl = response.fullurl; 55 | this.editurl = response.editurl; 56 | this.canonicalurl = response.canonicalurl; 57 | } 58 | 59 | /** 60 | * Returns the intro present in a wiki page 61 | * 62 | * @remarks 63 | * This method is part of the {@link Page | Page }. 64 | * 65 | * @param title - The title or page Id of the page 66 | * @param redirect - Whether to redirect in case of 302 67 | * @returns The intro string 68 | */ 69 | public intro = async (pageOptions?: pageOptions): Promise => { 70 | try { 71 | if (!this._intro) { 72 | const response = await intro(this.pageid.toString(), pageOptions?.redirect); 73 | this._intro = response; 74 | } 75 | return this._intro; 76 | } catch (error) { 77 | throw new introError(error); 78 | } 79 | } 80 | 81 | /** 82 | * Returns the images present in a wiki page 83 | * 84 | * @remarks 85 | * This method is part of the {@link Page | Page }. 86 | * 87 | * @param title - The title or page Id of the page 88 | * @param listOptions - {@link listOptions | listOptions } 89 | * @returns an array of imageResult {@link imageResult | imageResult } 90 | */ 91 | public images = async (listOptions?: listOptions): Promise> => { 92 | try { 93 | if (!this._images) { 94 | const result = await images(this.pageid.toString(), listOptions); 95 | this._images = result; 96 | } 97 | return this._images; 98 | } catch (error) { 99 | throw new imageError(error); 100 | } 101 | } 102 | 103 | /** 104 | * Returns the summary of the page 105 | * 106 | * @remarks 107 | * This method is part of the {@link Page | Page }. 108 | * 109 | * @param title - The title or page Id of the page 110 | * @param redirect - Whether to redirect in case of 302 111 | * @returns The summary of the page as {@link wikiSummary | wikiSummary} 112 | * 113 | */ 114 | public summary = async (pageOptions?: pageOptions): Promise => { 115 | try { 116 | if (!this._summary) { 117 | const result = await summary(this.title, pageOptions?.redirect); 118 | this._summary = result; 119 | } 120 | return this._summary; 121 | } catch (error) { 122 | throw new summaryError(error); 123 | } 124 | } 125 | 126 | /** 127 | * Returns the html content of a page 128 | * 129 | * @remarks 130 | * This method is part of the {@link Page | Page }. 131 | * 132 | * @param title - The title or page Id of the page 133 | * @param redirect - Whether to redirect in case of 302 134 | * @returns The html content as string 135 | * 136 | * @beta 137 | */ 138 | public html = async (pageOptions?: pageOptions): Promise => { 139 | try { 140 | if (!this._html) { 141 | const result = await html(this.pageid.toString(), pageOptions?.redirect); 142 | this._html = result; 143 | } 144 | return this._html; 145 | } catch (error) { 146 | throw new htmlError(error); 147 | } 148 | } 149 | 150 | /** 151 | * Returns the plain text content of a page and sets parent Id and rev Id 152 | * 153 | * @remarks 154 | * This method is part of the {@link Page | Page }. 155 | * 156 | * @param title - The title or page Id of the page 157 | * @param redirect - Whether to redirect in case of 302 158 | * @returns The plain text as string and the parent and revision ids 159 | */ 160 | public content = async (pageOptions?: pageOptions): Promise => { 161 | try { 162 | if (!this._content) { 163 | const result = await content(this.pageid.toString(), pageOptions?.redirect); 164 | this.parentid = result.ids.parentId; 165 | this.revid = result.ids.revId; 166 | this._content = result.result; 167 | } 168 | return this._content; 169 | } catch (error) { 170 | throw new contentError(error); 171 | } 172 | } 173 | 174 | /** 175 | * Returns the cetegories present in page 176 | * 177 | * @remarks 178 | * This method is part of the {@link Page | Page }. 179 | * 180 | * @param title - The title or page Id of the page 181 | * @param listOptions - {@link listOptions | listOptions } 182 | * @returns The categories as an array of string 183 | */ 184 | public categories = async (listOptions?: listOptions): Promise> => { 185 | try { 186 | if (!this._categories) { 187 | const result = await categories(this.pageid.toString(), listOptions); 188 | this._categories = result; 189 | } 190 | return this._categories; 191 | } catch (error) { 192 | throw new categoriesError(error); 193 | } 194 | } 195 | 196 | /** 197 | * Returns the links present in page 198 | * 199 | * @remarks 200 | * This method is part of the {@link Page | Page }. 201 | * 202 | * @param title - The title or page Id of the page 203 | * @param listOptions - {@link listOptions | listOptions } 204 | * @returns The links as an array of string 205 | */ 206 | public links = async (listOptions?: listOptions): Promise> => { 207 | try { 208 | if (!this._links) { 209 | const result = await links(this.pageid.toString(), listOptions); 210 | this._links = result; 211 | } 212 | return this._links; 213 | } catch (error) { 214 | throw new linksError(error); 215 | } 216 | } 217 | 218 | /** 219 | * Returns the references of external links present in page 220 | * 221 | * @remarks 222 | * This method is part of the {@link Page | Page }. 223 | * 224 | * @param title - The title or page Id of the page 225 | * @param listOptions - {@link listOptions | listOptions } 226 | * @returns The references as an array of string 227 | */ 228 | public references = async (listOptions?: listOptions): Promise> => { 229 | try { 230 | if (!this._references) { 231 | const result = await references(this.pageid.toString(), listOptions); 232 | this._references = result; 233 | } 234 | return this._references; 235 | } catch (error) { 236 | throw new linksError(error); 237 | } 238 | } 239 | 240 | /** 241 | * Returns the coordinates of a page 242 | * 243 | * @remarks 244 | * This method is part of the {@link Page | Page }. 245 | * 246 | * @param title - The title or page Id of the page 247 | * @param redirect - Whether to redirect in case of 302 248 | * @returns The coordinates as {@link coordinatesResult | coordinatesResult} 249 | */ 250 | public coordinates = async (pageOptions?: pageOptions): Promise => { 251 | try { 252 | if (!this._coordinates) { 253 | const result = await coordinates(this.pageid.toString(), pageOptions?.redirect); 254 | this._coordinates = result; 255 | } 256 | return this._coordinates; 257 | } catch (error) { 258 | throw new coordinatesError(error); 259 | } 260 | } 261 | 262 | /** 263 | * Returns the language links present in the page 264 | * 265 | * @remarks 266 | * This method is part of the {@link Page | Page }. 267 | * 268 | * @param title - The title or page Id of the page 269 | * @param listOptions - {@link listOptions | listOptions } 270 | * @returns The links as an array of {@link langLinksResult | langLinksResult } 271 | */ 272 | public langLinks = async (listOptions?: listOptions): Promise> => { 273 | try { 274 | if (!this._langLinks) { 275 | const result = await langLinks(this.pageid.toString(), listOptions); 276 | this._langLinks = result; 277 | } 278 | return this._langLinks; 279 | } catch (error) { 280 | throw new linksError(error); 281 | } 282 | } 283 | 284 | /** 285 | * Returns the infobox content of page if present 286 | * 287 | * @remarks 288 | * This method is part of the {@link Page | Page }. 289 | * 290 | * @param title - The title or page Id of the page 291 | * @param redirect - Whether to redirect in case of 302 292 | * @returns The info as JSON object 293 | */ 294 | public infobox = async (pageOptions?: pageOptions): Promise => { 295 | try { 296 | if (!this._infobox) { 297 | const result = await infobox(this.pageid.toString(), pageOptions?.redirect); 298 | this._infobox = result; 299 | } 300 | return this._infobox; 301 | } catch (error) { 302 | throw new infoboxError(error); 303 | } 304 | } 305 | 306 | /** 307 | * Returns the table content of page if present 308 | * 309 | * @remarks 310 | * This method is part of the {@link Page | Page }. 311 | * 312 | * @param title - The title or page Id of the page 313 | * @param redirect - Whether to redirect in case of 302 314 | * @returns The tables as arrays of JSON objects 315 | */ 316 | public tables = async (pageOptions?: pageOptions): Promise> => { 317 | try { 318 | if (!this._tables) { 319 | const result = await tables(this.pageid.toString(), pageOptions?.redirect); 320 | this._tables = result; 321 | } 322 | return this._tables; 323 | } catch (error) { 324 | throw new infoboxError(error); 325 | } 326 | } 327 | 328 | /** 329 | * Returns summaries for 20 pages related to the given page. Summaries include page title, namespace 330 | * and id along with short text description of the page and a thumbnail. 331 | * 332 | * @remarks 333 | * This method is part of the {@link Page | Page }. 334 | * 335 | * @param title - The title or page Id of the page 336 | * @param redirect - Whether to redirect in case of 302 337 | * @returns The related pages and summary as an array of {@link wikiSummary | wikiSummary} 338 | * 339 | * @experimental 340 | */ 341 | public related = async (pageOptions?: pageOptions): Promise => { 342 | try { 343 | if (!this._related) { 344 | const result = await related(this.title, pageOptions?.redirect); 345 | this._related = result; 346 | } 347 | return this._related; 348 | } catch (error) { 349 | throw new relatedError(error); 350 | } 351 | } 352 | 353 | /** 354 | * Gets the list of media items (images, audio, and video) in the 355 | * order in which they appear on a given wiki page. 356 | * 357 | * @remarks 358 | * Called in page object and also through index 359 | * 360 | * @param title - The title or page Id of the page 361 | * @param redirect - Whether to redirect in case of 302 362 | * @returns The related pages and summary as an array of {@link wikiMediaResult | wikiMediaResult} 363 | * 364 | * @experimental 365 | */ 366 | public media = async (pageOptions?: pageOptions): Promise => { 367 | try { 368 | if (!this._media) { 369 | const result = await media(this.title, pageOptions?.redirect); 370 | this._media = result; 371 | } 372 | return this._media; 373 | } catch (error) { 374 | throw new mediaError(error); 375 | } 376 | } 377 | 378 | /** 379 | * Returns mobile-optimised HTML of a page 380 | * 381 | * @param title - The title of the page to query 382 | * @param redirect - Whether to redirect in case of 302 383 | * @returns Returns HTML string 384 | */ 385 | public mobileHtml = async (pageOptions?: pageOptions): Promise => { 386 | try { 387 | if (!this._mobileHtml) { 388 | const result = await mobileHtml(this.title, pageOptions?.redirect); 389 | this._mobileHtml = result; 390 | } 391 | return this._mobileHtml; 392 | } catch (error) { 393 | throw new htmlError(error); 394 | } 395 | } 396 | 397 | /** 398 | * Returns pdf of a given page 399 | * 400 | * @param pdfOptions - {@link pdfOptions | pdfOptions } 401 | * @returns Returns path string 402 | */ 403 | public pdf = async (pdfOptions?: pdfOptions): Promise => { 404 | try { 405 | const result = await pdf(this.title, pdfOptions) 406 | 407 | return result; 408 | } catch (error) { 409 | throw new pdfError(error); 410 | } 411 | } 412 | 413 | public async runMethod(functionName: string): Promise { 414 | try { 415 | const result = await eval(`this.${functionName}()`); 416 | return result; 417 | } catch (error) { 418 | throw new preloadError(error); 419 | } 420 | } 421 | } 422 | 423 | /** 424 | * Returns the images present in a wiki page 425 | * 426 | * @remarks 427 | * Called in page object and also through wiki default object 428 | * 429 | * @param title - The title or page Id of the page 430 | * @param listOptions - {@link listOptions | listOptions } 431 | * @returns an array of imageResult {@link imageResult | imageResult } 432 | */ 433 | export const images = async (title: string, listOptions?: listOptions): Promise> => { 434 | try { 435 | let imageOptions: any = { 436 | generator: 'images', 437 | gimlimit: listOptions?.limit || 5, 438 | prop: 'imageinfo', 439 | iiprop: 'url' 440 | } 441 | imageOptions = setPageIdOrTitleParam(imageOptions, title); 442 | const response = await request(imageOptions, listOptions?.redirect); 443 | const images = []; 444 | const imageKeys = Object.keys(response.query.pages); 445 | for (const image of imageKeys) { 446 | const imageInfo = response.query.pages[image]; 447 | imageInfo.url = imageInfo.imageinfo[0].url; 448 | images.push(imageInfo); 449 | } 450 | return images; 451 | } catch (error) { 452 | throw new imageError(error); 453 | } 454 | } 455 | 456 | /** 457 | * Returns the intro present in a wiki page 458 | * 459 | * @remarks 460 | * Called in page object and also through wiki default object 461 | * 462 | * @param title - The title or page Id of the page 463 | * @param redirect - Whether to redirect in case of 302 464 | * @returns The intro string 465 | */ 466 | export const intro = async (title: string, redirect = true): Promise => { 467 | try { 468 | let introOptions: any = { 469 | prop: 'extracts', 470 | explaintext: '', 471 | exintro: '', 472 | } 473 | introOptions = setPageIdOrTitleParam(introOptions, title); 474 | const response = await request(introOptions, redirect); 475 | const pageId = setPageId(introOptions, response); 476 | return response?.query?.pages[pageId].extract; 477 | } catch (error) { 478 | throw new introError(error); 479 | } 480 | } 481 | 482 | /** 483 | * Returns the html content of a page 484 | * 485 | * @remarks 486 | * Called in page object and also through wiki default object 487 | * 488 | * @param title - The title or page Id of the page 489 | * @param redirect - Whether to redirect in case of 302 490 | * @returns The html content as string 491 | * 492 | * @beta 493 | */ 494 | export const html = async (title: string, redirect = true): Promise => { 495 | try { 496 | let htmlOptions: any = { 497 | 'prop': 'revisions', 498 | 'rvprop': 'content', 499 | 'rvlimit': 1, 500 | 'rvparse': '' 501 | } 502 | htmlOptions = setPageIdOrTitleParam(htmlOptions, title); 503 | const response = await request(htmlOptions, redirect); 504 | const pageId = setPageId(htmlOptions, response); 505 | return response.query.pages[pageId].revisions[0]['*']; 506 | } catch (error) { 507 | throw new htmlError(error); 508 | } 509 | } 510 | 511 | /** 512 | * Returns the plain text content of a page as well as parent id and revision id 513 | * 514 | * @remarks 515 | * Called in page object and also through wiki default object 516 | * 517 | * @param title - The title or page Id of the page 518 | * @param redirect - Whether to redirect in case of 302 519 | * @returns The plain text as string and the parent and revision ids 520 | */ 521 | export const content = async (title: string, redirect = true): Promise => { 522 | try { 523 | let contentOptions: any = { 524 | 'prop': 'extracts|revisions', 525 | 'explaintext': '', 526 | 'rvprop': 'ids' 527 | } 528 | contentOptions = setPageIdOrTitleParam(contentOptions, title); 529 | const response = await request(contentOptions, redirect); 530 | const pageId = setPageId(contentOptions, response); 531 | const result = response['query']['pages'][pageId]['extract']; 532 | const ids = { 533 | revisionId: response['query']['pages'][pageId]['revisions'][0]['revid'], 534 | parentId: response['query']['pages'][pageId]['revisions'][0]['parentid'] 535 | } 536 | return { 537 | result, 538 | ids 539 | } 540 | } catch (error) { 541 | throw new contentError(error); 542 | } 543 | } 544 | 545 | /** 546 | * Returns the cetegories present in page 547 | * 548 | * @remarks 549 | * Called in page object and also through wiki default object 550 | * 551 | * @param title - The title or page Id of the page 552 | * @param listOptions - {@link listOptions | listOptions } 553 | * @returns The categories as an array of string 554 | */ 555 | export const categories = async (title: string, listOptions?: listOptions): Promise> => { 556 | try { 557 | let categoryOptions: any = { 558 | prop: 'categories', 559 | pllimit: listOptions?.limit, 560 | } 561 | categoryOptions = setPageIdOrTitleParam(categoryOptions, title); 562 | const response = await request(categoryOptions, listOptions?.redirect); 563 | const pageId = setPageId(categoryOptions, response); 564 | return response.query.pages[pageId].categories.map((category: any) => category.title) 565 | } catch (error) { 566 | throw new categoriesError(error); 567 | } 568 | } 569 | 570 | /** 571 | * Returns the links present in page 572 | * 573 | * @remarks 574 | * Called in page object and also through wiki default object 575 | * 576 | * @param title - The title or page Id of the page 577 | * @param listOptions - {@link listOptions | listOptions } 578 | * @returns The links as an array of string 579 | */ 580 | export const links = async (title: string, listOptions?: listOptions): Promise> => { 581 | try { 582 | let linksOptions: any = { 583 | prop: 'links', 584 | plnamespace: 0, 585 | pllimit: listOptions?.limit || 'max', 586 | } 587 | linksOptions = setPageIdOrTitleParam(linksOptions, title); 588 | const response = await request(linksOptions, listOptions?.redirect); 589 | const pageId = setPageId(linksOptions, response); 590 | const result = response.query.pages[pageId].links.map((link: any) => link.title) 591 | return result; 592 | } catch (error) { 593 | throw new linksError(error); 594 | } 595 | } 596 | 597 | /** 598 | * Returns the references of external links present in page 599 | * 600 | * @remarks 601 | * Called in page object and also through wiki default object 602 | * 603 | * @param title - The title or page Id of the page 604 | * @param listOptions - {@link listOptions | listOptions } 605 | * @returns The references as an array of string 606 | */ 607 | export const references = async (title: string, listOptions?: listOptions): Promise> => { 608 | try { 609 | let extLinksOptions: any = { 610 | prop: 'extlinks', 611 | ellimit: listOptions?.limit || 'max', 612 | } 613 | extLinksOptions = setPageIdOrTitleParam(extLinksOptions, title); 614 | const response = await request(extLinksOptions, listOptions?.redirect); 615 | const pageId = setPageId(extLinksOptions, response); 616 | const result = response.query.pages[pageId].extlinks.map((link: any) => link['*']) 617 | return result; 618 | } catch (error) { 619 | throw new linksError(error); 620 | } 621 | } 622 | 623 | /** 624 | * Returns the coordinates of a page 625 | * 626 | * @remarks 627 | * Called in page object and also through wiki default object 628 | * 629 | * @param title - The title or page Id of the page 630 | * @param redirect - Whether to redirect in case of 302 631 | * @returns The coordinates as {@link coordinatesResult | coordinatesResult} 632 | */ 633 | export const coordinates = async (title: string, redirect = true): Promise => { 634 | try { 635 | let coordinatesOptions: any = { 636 | prop: 'coordinates', 637 | } 638 | coordinatesOptions = setPageIdOrTitleParam(coordinatesOptions, title); 639 | const response = await request(coordinatesOptions, redirect); 640 | const pageId = setPageId(coordinatesOptions, response); 641 | const coordinates = response.query.pages[pageId].coordinates; 642 | return coordinates ? coordinates[0] : null; 643 | } catch (error) { 644 | throw new coordinatesError(error); 645 | } 646 | } 647 | 648 | /** 649 | * Returns the language links present in the page 650 | * 651 | * @remarks 652 | * Called in page object and also through wiki default object 653 | * 654 | * @param title - The title or page Id of the page 655 | * @param listOptions - {@link listOptions | listOptions } 656 | * @returns The links as an array of {@link langLinksResult | langLinksResult } 657 | */ 658 | export const langLinks = async (title: string, listOptions?: listOptions): Promise> => { 659 | try { 660 | let languageOptions: any = { 661 | prop: 'langlinks', 662 | lllimit: listOptions?.limit || 'max', 663 | llprop: 'url' 664 | } 665 | languageOptions = setPageIdOrTitleParam(languageOptions, title); 666 | const response = await request(languageOptions, listOptions?.redirect); 667 | const pageId = setPageId(languageOptions, response); 668 | const result = (response.query.pages[pageId].langlinks ?? []).map((link: any) => { 669 | return { 670 | lang: link.lang, 671 | title: link['*'], 672 | url: link.url 673 | }; 674 | }) 675 | return result; 676 | } catch (error) { 677 | throw new linksError(error); 678 | } 679 | } 680 | 681 | /** 682 | * Returns the infobox content of page if present 683 | * 684 | * @remarks 685 | * Called in page object and also through wiki default object 686 | * 687 | * @param title - The title or page Id of the page 688 | * @param redirect - Whether to redirect in case of 302 689 | * @returns The info as JSON object 690 | */ 691 | export const infobox = async (title: string, redirect = true): Promise => { 692 | try { 693 | const infoboxOptions: any = { 694 | prop: 'revisions', 695 | rvprop: 'content', 696 | rvsection: 0 697 | } 698 | const fullInfo = await rawInfo(title, infoboxOptions, redirect); 699 | const info = infoboxParser(fullInfo).general; 700 | return info; 701 | } catch (error) { 702 | throw new infoboxError(error); 703 | } 704 | } 705 | 706 | /** 707 | * Returns the table content of page if present 708 | * 709 | * @remarks 710 | * Called in page object and also through wiki default object 711 | * 712 | * @param title - The title or page Id of the page 713 | * @param redirect - Whether to redirect in case of 302 714 | * @returns The tables as arrays of JSON objects 715 | */ 716 | export const tables = async (title: string, redirect = true): Promise> => { 717 | try { 718 | const tableOptions: any = { 719 | prop: 'revisions', 720 | rvprop: 'content', 721 | } 722 | const fullInfo = await rawInfo(title, tableOptions, redirect); 723 | const info = infoboxParser(fullInfo).tables; 724 | return info; 725 | } catch (error) { 726 | throw new infoboxError(error); 727 | } 728 | } 729 | 730 | /** 731 | * Returns the raw info of the page 732 | * 733 | * @remarks 734 | * This is not exported and used internally 735 | * 736 | * @param title - The title or page Id of the page 737 | * @param redirect - Whether to redirect in case of 302 738 | * @returns The rawInfo of the page 739 | * 740 | */ 741 | export const rawInfo = async (title: string, options: any, redirect = true): Promise => { 742 | try { 743 | options = setPageIdOrTitleParam(options, title); 744 | const response = await request(options, redirect); 745 | if (!(response.query?.pages)) { 746 | throw new wikiError(MSGS.INFOBOX_NOT_EXIST); 747 | } 748 | const pageId = setPageId(options, response); 749 | const data = response.query.pages[pageId]['revisions'][0]; 750 | return data ? data['*'] : ''; 751 | } catch (error) { 752 | throw new infoboxError(error); 753 | } 754 | } 755 | 756 | //REST-API Requests based on https://en.wikipedia.org/api/rest_v1/#/ 757 | //APIs seems to support only title parameters which is a drawback 758 | 759 | /** 760 | * Returns the summary of the page 761 | * 762 | * @remarks 763 | * Called in page object and also through wiki default object 764 | * 765 | * @param title - The title or page Id of the page 766 | * @param redirect - Whether to redirect in case of 302 767 | * @returns The summary of the page as {@link wikiSummary | wikiSummary} 768 | */ 769 | export const summary = async (title: string, redirect = true): Promise => { 770 | try { 771 | const path = 'page/summary/' + title.replace(" ", "_"); 772 | const response = await makeRestRequest(path, redirect); 773 | return response; 774 | } catch (error) { 775 | throw new summaryError(error); 776 | } 777 | } 778 | 779 | /** 780 | * Returns summaries for 20 pages related to the given page. Summaries include page title, namespace 781 | * and id along with short text description of the page and a thumbnail. 782 | * 783 | * @remarks 784 | * Called in page object and also through index 785 | * 786 | * @param title - The title or page Id of the page 787 | * @param redirect - Whether to redirect in case of 302 788 | * @returns The related pages and summary as an array of {@link wikiSummary | wikiSummary} 789 | * 790 | * @experimental 791 | */ 792 | export const related = async (title: string, redirect = true): Promise => { 793 | try { 794 | const path = 'page/related/' + title.replace(" ", "_"); 795 | const response = await makeRestRequest(path, redirect); 796 | return response; 797 | } catch (error) { 798 | throw new relatedError(error); 799 | } 800 | } 801 | 802 | /** 803 | * Gets the list of media items (images, audio, and video) in the 804 | * order in which they appear on a given wiki page. 805 | * 806 | * @remarks 807 | * Called in page object and also through index 808 | * 809 | * @param title - The title or page Id of the page 810 | * @param redirect - Whether to redirect in case of 302 811 | * @returns The related pages and summary as an array of {@link wikiMediaResult | wikiMediaResult} 812 | * 813 | * @experimental 814 | */ 815 | export const media = async (title: string, redirect = true): Promise => { 816 | try { 817 | const path = 'page/media-list/' + title.replace(" ", "_"); 818 | const response = await makeRestRequest(path, redirect); 819 | return response; 820 | } catch (error) { 821 | throw new mediaError(error); 822 | } 823 | } 824 | 825 | /** 826 | * Returns mobile-optimised HTML of a page 827 | * 828 | * @param title - The title of the page to query 829 | * @param redirect - Whether to redirect in case of 302 830 | * @returns Returns HTML string 831 | */ 832 | export const mobileHtml = async (title: string, redirect = true): Promise => { 833 | try { 834 | const path = `page/mobile-html/${title}`; 835 | const result = await makeRestRequest(path, redirect); 836 | return result; 837 | } catch (error) { 838 | throw new htmlError(error); 839 | } 840 | } 841 | 842 | /** 843 | * Returns pdf of a given page 844 | * 845 | * @param title - The title of the page to query 846 | * @param pdfOptions - {@link pdfOptions | pdfOptions } 847 | * @returns Returns pdf format 848 | */ 849 | export const pdf = async (title: string, pdfOptions?: pdfOptions): Promise => { 850 | try { 851 | let path = `page/pdf/${title}`; 852 | pdfOptions?.format ? path += `/${pdfOptions.format}` : null; 853 | pdfOptions?.type ? path += `/${pdfOptions.type}` : null; 854 | 855 | const result = returnRestUrl(path); 856 | return result; 857 | } catch (error) { 858 | throw new pdfError(error); 859 | } 860 | } 861 | 862 | /** 863 | * Returns citation of a given page, or query string 864 | * 865 | * @param format - the format of the citation result 866 | * @param query - url or query string 867 | * @param language - if you want lanuage enabled results 868 | * @returns Returns ciation data 869 | */ 870 | export const citation = async (query: string, format?: citationFormat, language?: string): Promise => { 871 | try { 872 | let path = `data/citation`; 873 | path += format ? `/${format}` : `/mediawiki`; 874 | path += `/${query}`; 875 | language ? path += `/${language}` : null; 876 | 877 | const result = await makeRestRequest(path); 878 | return result; 879 | } catch (error) { 880 | throw new citationError(error); 881 | } 882 | } 883 | 884 | export default Page; 885 | -------------------------------------------------------------------------------- /source/request.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosRequestConfig } from 'axios'; 2 | import { wikiError } from './errors'; 3 | 4 | let API_URL = 'https://en.wikipedia.org/w/api.php?', 5 | REST_API_URL = 'https://en.wikipedia.org/api/rest_v1/', 6 | // RATE_LIMIT = false, 7 | // RATE_LIMIT_MIN_WAIT = undefined, 8 | // RATE_LIMIT_LAST_CALL = undefined, 9 | USER_AGENT = 'wikipedia (https://github.com/dopecodez/Wikipedia/)'; 10 | 11 | async function callAPI(url: string) { 12 | const options: AxiosRequestConfig = { 13 | headers: { 14 | "Api-User-Agent": USER_AGENT, 15 | }, 16 | }; 17 | try { 18 | const { data } = await axios.get(url, options); 19 | return data; 20 | } catch (error) { 21 | throw new wikiError(error); 22 | } 23 | } 24 | 25 | // Makes a request to legacy php endpoint 26 | async function makeRequest(params: any, redirect = true): Promise { 27 | const search = { ...params }; 28 | search['format'] = 'json'; 29 | if (redirect) { 30 | search['redirects'] = ''; 31 | } 32 | if (!params.action) { 33 | search['action'] = "query"; 34 | } 35 | search['origin'] = '*'; 36 | let searchParam = ''; 37 | Object.keys(search).forEach(key => { 38 | searchParam += `${key}=${search[key]}&`; 39 | }); 40 | 41 | return await callAPI(encodeURI(API_URL + searchParam)); 42 | } 43 | 44 | // Makes a request to rest api endpoint 45 | export async function makeRestRequest(path: string, redirect = true): Promise { 46 | if (!redirect) { 47 | path += '?redirect=false'; 48 | } 49 | 50 | return await callAPI(encodeURI(REST_API_URL + path)); 51 | } 52 | 53 | //return rest uri 54 | export function returnRestUrl(path: string): string { 55 | return encodeURI(REST_API_URL + path); 56 | } 57 | 58 | //change language of both urls 59 | export function setAPIUrl(prefix: string) : string { 60 | API_URL = 'https://' + prefix.toLowerCase() + '.wikipedia.org/w/api.php?'; 61 | REST_API_URL = 'https://' + prefix.toLowerCase() + '.wikipedia.org/api/rest_v1/'; 62 | return API_URL; 63 | } 64 | 65 | //change user agent 66 | export function setUserAgent(userAgent: string) { 67 | USER_AGENT = userAgent; 68 | } 69 | 70 | export default makeRequest; 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /source/resultTypes.ts: -------------------------------------------------------------------------------- 1 | export interface wikiSearchResult { 2 | results: any[], 3 | suggestion: string 4 | } 5 | 6 | export interface pageResult { 7 | pageid: number, 8 | ns: number, 9 | title: string, 10 | contentmodel: string, 11 | pagelanguage: string, 12 | pagelanguagehtmlcode: string, 13 | pagelanguagedir: string, 14 | touched: string, 15 | lastrevid: number, 16 | length: number, 17 | fullurl: string, 18 | editurl: string, 19 | canonicalurl: string 20 | } 21 | 22 | export interface imageResult { 23 | pageid: number, 24 | ns: number, 25 | title: string, 26 | imagerepository: string, 27 | imageinfo: any, 28 | url: string 29 | } 30 | 31 | export interface languageResult { 32 | [key: string]: string 33 | } 34 | 35 | export interface geoSearchResult { 36 | pageid: number, 37 | ns: number, 38 | title: string, 39 | lat: number, 40 | lon: number, 41 | dist: number, 42 | primary: string, 43 | type: string 44 | } 45 | 46 | export interface coordinatesResult { 47 | lat: number 48 | lon: number, 49 | primary: string, 50 | globe: string 51 | } 52 | 53 | export interface langLinksResult { 54 | lang: string, 55 | title: string, 56 | url: string 57 | } 58 | 59 | export interface wikiSummary { 60 | ns?: number, 61 | index?: number, 62 | type: string, 63 | title: string, 64 | displaytitle: string, 65 | namespace: { id: number, text: string }, 66 | wikibase_item: string, 67 | titles: { canonical: string, normalized: string, display: string }, 68 | pageid: number, 69 | thumbnail: { 70 | source: string, 71 | width: number, 72 | height: number 73 | }, 74 | originalimage: { 75 | source: string, 76 | width: number, 77 | height: number 78 | }, 79 | lang: string, 80 | dir: string, 81 | revision: string, 82 | tid: string, 83 | timestamp: string, 84 | description: string, 85 | description_source: string, 86 | content_urls: { 87 | desktop: { 88 | page: string, 89 | revisions: string, 90 | edit: string, 91 | talk: string 92 | }, 93 | mobile: { 94 | page: string, 95 | revisions: string, 96 | edit: string, 97 | talk: string 98 | } 99 | }, 100 | extract: string 101 | extract_html: string 102 | normalizedtitle?: string, 103 | coordinates?: { 104 | lat: number, 105 | lon: number 106 | } 107 | } 108 | 109 | export interface MostRead extends wikiSummary { 110 | views: number; 111 | rank: number; 112 | view_history: [ 113 | { 114 | date: string; 115 | views: number; 116 | } 117 | ] 118 | } 119 | 120 | export interface wikiMediaResult { 121 | revision: string, 122 | tid: string, 123 | items: Array 124 | } 125 | 126 | export interface mediaResult { 127 | title: string, 128 | section_id: number, 129 | type: string, 130 | caption?: htmlText, 131 | showInGallery: boolean, 132 | srcset: Array 133 | } 134 | 135 | export interface srcResult { 136 | src: string, 137 | scale: string 138 | } 139 | 140 | export interface eventResult { 141 | births?: [ 142 | { 143 | text: string, 144 | pages: Array 145 | year?: number 146 | } 147 | ], 148 | deaths?: [ 149 | { 150 | text: string, 151 | pages: Array, 152 | year?: number 153 | } 154 | ], 155 | events?: [ 156 | { 157 | text: string, 158 | pages: Array, 159 | year?: number 160 | } 161 | ], 162 | holidays?: [ 163 | { 164 | text: string, 165 | pages: Array 166 | } 167 | ], 168 | selected?: [ 169 | { 170 | text: string, 171 | pages: Array, 172 | year?: number 173 | } 174 | ] 175 | } 176 | 177 | export interface titleItem { 178 | title: string, 179 | page_id: number, 180 | rev: number, 181 | tid: number, 182 | namespace: number, 183 | user_id: number, 184 | user_text: string, 185 | timestamp: string, 186 | comment: string, 187 | tags: Array, 188 | restrictions: Array, 189 | page_language: string, 190 | redirect: boolean 191 | } 192 | 193 | export interface title { 194 | items: Array 195 | } 196 | 197 | export interface relatedResult { 198 | pages: Array 199 | } 200 | 201 | export interface mobileSections { 202 | lead: { 203 | ns: number, 204 | id: number, 205 | revision: string, 206 | lastmodified: string, 207 | lastmodifier: { 208 | user: string, 209 | gender: string 210 | }, 211 | displaytitle: string, 212 | normalizedtitle: string, 213 | wikibase_item: string, 214 | description: string, 215 | description_source: string, 216 | protection: Record, 217 | editable: boolean, 218 | languagecount: number, 219 | image: { 220 | file: string, 221 | urls: { 222 | 320: string, 223 | 640: string, 224 | 800: string, 225 | 1024: string 226 | } 227 | }, 228 | issues: Array, 229 | geo?: { 230 | latitude: string, 231 | longitude: string 232 | } 233 | sections: Array
234 | }, 235 | remaining: { 236 | sections: Array
237 | } 238 | } 239 | 240 | export interface section { 241 | id: number, 242 | text: string, 243 | toclevel: number, 244 | line: string, 245 | anchor: string 246 | } 247 | 248 | export interface htmlText { 249 | html: string, 250 | text: string 251 | } 252 | 253 | export interface Artist extends htmlText { 254 | name: string; 255 | user_page?: string; 256 | } 257 | 258 | export interface Description extends htmlText { 259 | lang: string; 260 | } 261 | 262 | export interface notFound { 263 | type: string, 264 | title: string, 265 | method: string, 266 | detail: string, 267 | uri: string 268 | } 269 | 270 | export interface featuredContentResult { 271 | tfa: wikiSummary; 272 | mostread: { 273 | date: string; 274 | articles: Array 275 | }; 276 | image: { 277 | title: string; 278 | thumbnail: { 279 | source: string; 280 | width: number; 281 | height: number; 282 | }; 283 | image: { 284 | source: string; 285 | width: number; 286 | height: number; 287 | }; 288 | file_page: string; 289 | artist: Artist; 290 | credit: htmlText; 291 | license: { 292 | type: string; 293 | code: string; 294 | }; 295 | description: Description; 296 | wb_entity_id: string; 297 | structured: { 298 | captions: { 299 | [key: string]: string; 300 | } 301 | }; 302 | }; 303 | news: [ 304 | { 305 | links: Array; 306 | story: string; 307 | } 308 | ]; 309 | onthisday: [ 310 | { 311 | text: string; 312 | pages: Array; 313 | year: number; 314 | } 315 | ] 316 | } -------------------------------------------------------------------------------- /source/utils.ts: -------------------------------------------------------------------------------- 1 | import wiki from "."; 2 | import { pageError } from "./errors"; 3 | import { MSGS } from "./messages"; 4 | 5 | //check if input is string 6 | export function isString(title: any){ 7 | return isNaN(title); 8 | } 9 | 10 | //set title for page in case autoSuggest is true 11 | export async function setTitleForPage(title: string) { 12 | { 13 | const searchResult = await wiki.search(title, { limit: 1, suggestion: true }) 14 | if (!searchResult.suggestion && searchResult.results.length == 0) { 15 | throw new pageError(`${MSGS.PAGE_NOT_SUGGEST}${title}`) 16 | } 17 | title = searchResult.suggestion || title; 18 | return title; 19 | } 20 | } 21 | 22 | //Set page id or title param for legacy api queries 23 | export function setPageIdOrTitleParam(params: any, title: string) { 24 | if (isString(title)) { 25 | params.titles = title 26 | } else { 27 | params.pageids = title 28 | } 29 | return params; 30 | } 31 | 32 | //Get page id from params or from results 33 | export function setPageId(params: any, results: any): number { 34 | let pageId; 35 | if (params.pageIds) { 36 | pageId = params.pageIds; 37 | } else { 38 | pageId = Object.keys(results.query.pages)[0]; 39 | } 40 | return pageId; 41 | } 42 | 43 | //Get current year 44 | export function getCurrentYear(): number { 45 | const date = new Date(); 46 | const year = date.getFullYear(); 47 | return (year); 48 | } 49 | 50 | //Get current month 51 | export function getCurrentMonth(): number { 52 | const date = new Date(); 53 | const month = date.getMonth(); 54 | return (month + 1); //javascript months are indexed at zero for some reason 55 | } 56 | 57 | //Get current day 58 | export function getCurrentDay(): number { 59 | const date = new Date(); 60 | const day = date.getDate(); 61 | return day; 62 | } -------------------------------------------------------------------------------- /test/categories.test.ts: -------------------------------------------------------------------------------- 1 | import { categoriesError } from '../source/errors'; 2 | import Page, { categories } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const categoryMock = { 11 | 500: { categories: [{title:"Category1"}, {title: "Category2"}] } 12 | } 13 | 14 | const categoryResult = ["Category1", "Category2"] 15 | 16 | afterAll(() => { 17 | requestMock.mockRestore(); 18 | setTitleMock.mockRestore(); 19 | }) 20 | 21 | test('Category method on page object returns without calling request if _categories field set', async () => { 22 | requestMock.mockImplementation(async () => { return { query: { pages: categoryMock } } }); 23 | const page = new Page(pageJson); 24 | page._categories = [] 25 | const result = await page.categories(); 26 | expect(requestMock).toHaveBeenCalledTimes(0); 27 | expect(result).toStrictEqual(page._categories); 28 | }); 29 | 30 | test('category method on page object returns array of strings', async () => { 31 | requestMock.mockImplementation(async () => { return { query: { pages: categoryMock } } }); 32 | const page = new Page(pageJson); 33 | const result = await page.categories(); 34 | expect(requestMock).toHaveBeenCalledTimes(1); 35 | expect(result).toStrictEqual(categoryResult); 36 | }); 37 | 38 | test('category method on page throws category error if response is empty', async () => { 39 | requestMock.mockImplementation(async () => { return [] }); 40 | const page = new Page(pageJson); 41 | const t = async () => { 42 | await page.categories() 43 | }; 44 | expect(t).rejects.toThrowError(categoriesError); 45 | }); 46 | 47 | test('Throws category error if response is empty', async () => { 48 | requestMock.mockImplementation(async () => { return [] }); 49 | const t = async () => { 50 | await categories("Test") 51 | }; 52 | expect(t).rejects.toThrowError(categoriesError); 53 | }); 54 | 55 | test('Returns empty if no categories are available', async () => { 56 | requestMock.mockImplementation(async () => { return { query: { pages: {500: {categories:[]}} } } }); 57 | const result = await categories("Test"); 58 | expect(result).toStrictEqual([]); 59 | }); 60 | 61 | test('Returns with results an array of string', async () => { 62 | requestMock.mockImplementation(async () => { return { query: { pages: categoryMock } } }); 63 | const result = await categories("Test"); 64 | expect(result).toStrictEqual(categoryResult); 65 | }); 66 | 67 | test('category method on index throws category error if response is empty', async () => { 68 | requestMock.mockImplementation(async () => { return [] }); 69 | const t = async () => { 70 | await wiki.categories("Test"); 71 | }; 72 | expect(t).rejects.toThrowError(categoriesError); 73 | }); 74 | 75 | test('categories method on index returns array of strings', async () => { 76 | requestMock.mockImplementation(async () => { return { query: { pages: categoryMock } } }); 77 | const result = await wiki.categories("Test"); 78 | expect(setTitleMock).toHaveBeenCalledTimes(0); 79 | expect(result).toStrictEqual(categoryResult); 80 | }); 81 | 82 | test('category method on index returns array of strings even when autosuggest is true', async () => { 83 | requestMock.mockImplementation(async () => { return { query: { pages: categoryMock } } }); 84 | setTitleMock.mockImplementation(async () => { return "test" }); 85 | const result = await wiki.categories("Test", { autoSuggest: true }); 86 | expect(setTitleMock).toHaveBeenCalledTimes(1); 87 | expect(result).toStrictEqual(categoryResult); 88 | }); -------------------------------------------------------------------------------- /test/citation.test.ts: -------------------------------------------------------------------------------- 1 | import * as request from '../source/request'; 2 | import wiki, { citationError } from "../source/index"; 3 | import { mobileSections, citationData, title } from './samples'; 4 | import Page, { citation } from '../source/page'; 5 | import { pageJson } from './samples'; 6 | const requestMock = jest.spyOn(request, "makeRestRequest"); 7 | 8 | afterAll(() => { 9 | requestMock.mockRestore(); 10 | }) 11 | 12 | test('Throws error if response is error', async () => { 13 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 14 | const err = async () => { await wiki.citation("test", "mediawiki", "fr") }; 15 | expect(err).rejects.toThrowError(citationError); 16 | }); 17 | 18 | test('Returns citation data', async () => { 19 | requestMock.mockImplementation(async () => { return citationData }); 20 | const result = await wiki.citation("test", "mediawiki"); 21 | expect(result).toStrictEqual(citationData); 22 | }); 23 | 24 | test('Returns summary and format as wikimedia by default', async () => { 25 | requestMock.mockImplementation(async () => { return citationData }); 26 | const result = await wiki.citation("test"); 27 | expect(result).toStrictEqual(citationData); 28 | }); 29 | 30 | test('Throws category error if response is empty', async () => { 31 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 32 | const t = async () => { 33 | await citation("Test") 34 | }; 35 | expect(t).rejects.toThrowError(citationError); 36 | }); 37 | 38 | test('Returns citation data', async () => { 39 | requestMock.mockImplementation(async () => { return citationData }); 40 | const result = await citation("Test"); 41 | expect(result).toStrictEqual(citationData); 42 | }); -------------------------------------------------------------------------------- /test/content.test.ts: -------------------------------------------------------------------------------- 1 | import { contentError } from '../source/errors'; 2 | import Page, { content } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const contentMock = { 11 | 500: { revisions: [{ revid: "100", parentid: "200" }], 12 | extract: "This is the content" } 13 | } 14 | 15 | const contentResult = "This is the content" 16 | 17 | const contentWithIds = { 18 | result: contentResult, 19 | ids: { 20 | revisionId: "100", 21 | parentId: "200" 22 | } 23 | } 24 | 25 | afterAll(() => { 26 | requestMock.mockRestore(); 27 | setTitleMock.mockRestore(); 28 | }) 29 | 30 | test('content method on page object returns without calling request if _content field set', async () => { 31 | requestMock.mockImplementation(async () => { return { query: { pages: contentMock } } }); 32 | const page = new Page(pageJson); 33 | page._content = "Test content" 34 | const result = await page.content(); 35 | expect(requestMock).toHaveBeenCalledTimes(0); 36 | expect(result).toStrictEqual(page._content); 37 | }); 38 | 39 | test('content method on page object returns content', async () => { 40 | requestMock.mockImplementation(async () => { return { query: { pages: contentMock } } }); 41 | const page = new Page(pageJson); 42 | const result = await page.content({redirect: true}); 43 | expect(requestMock).toHaveBeenCalledTimes(1); 44 | expect(result).toStrictEqual(contentResult); 45 | }); 46 | 47 | test('content method on page throws content error if response is empty', async () => { 48 | requestMock.mockImplementation(async () => { return [] }); 49 | const page = new Page(pageJson); 50 | const t = async () => { 51 | await page.content(); 52 | }; 53 | expect(t).rejects.toThrowError(contentError); 54 | }); 55 | 56 | test('Throws content error if response is empty', async () => { 57 | requestMock.mockImplementation(async () => { return [] }); 58 | const t = async () => { 59 | await content("Test") 60 | }; 61 | expect(t).rejects.toThrowError(contentError); 62 | }); 63 | 64 | test('Returns with results as string', async () => { 65 | requestMock.mockImplementation(async () => { return { query: { pages: contentMock } } }); 66 | const result = await content("Test"); 67 | expect(result).toStrictEqual(contentWithIds); 68 | }); 69 | 70 | test('content method on index throws content error if response is empty', async () => { 71 | requestMock.mockImplementation(async () => { return [] }); 72 | const t = async () => { 73 | await wiki.content("Test") 74 | }; 75 | expect(t).rejects.toThrowError(contentError); 76 | }); 77 | 78 | test('content method on index returns a string', async () => { 79 | requestMock.mockImplementation(async () => { return { query: { pages: contentMock } } }); 80 | const result = await wiki.content("Test"); 81 | expect(setTitleMock).toHaveBeenCalledTimes(0); 82 | expect(result).toStrictEqual(contentResult); 83 | }); 84 | 85 | test('content method on index returns a string even when autosuggest is true', async () => { 86 | requestMock.mockImplementation(async () => { return { query: { pages: contentMock } } }); 87 | setTitleMock.mockImplementation(async () => { return "test" }); 88 | const result = await wiki.content("Test", { autoSuggest: true }); 89 | expect(setTitleMock).toHaveBeenCalledTimes(1); 90 | expect(result).toStrictEqual(contentResult); 91 | }); -------------------------------------------------------------------------------- /test/coordinates.test.ts: -------------------------------------------------------------------------------- 1 | import { coordinatesError } from '../source/errors'; 2 | import Page, { coordinates } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const coordinatesMock = { 11 | 500: { coordinates: [{lat: 51.17, lon: 30.15, primary: "", globe: "earth"}] } 12 | } 13 | 14 | const coordinatesResult = {lat: 51.17, lon: 30.15, primary: "", globe: "earth"} 15 | 16 | afterAll(() => { 17 | requestMock.mockRestore(); 18 | setTitleMock.mockRestore(); 19 | }) 20 | 21 | test('Coordinates method on page object returns without calling request if _coordinates field set', async () => { 22 | requestMock.mockImplementation(async () => { return { query: { pages: coordinatesMock } } }); 23 | const page = new Page(pageJson); 24 | page._coordinates = coordinatesResult; 25 | const result = await page.coordinates(); 26 | expect(requestMock).toHaveBeenCalledTimes(0); 27 | expect(result).toStrictEqual(page._coordinates); 28 | }); 29 | 30 | test('Coordinates method on page object returns coordinates', async () => { 31 | requestMock.mockImplementation(async () => { return { query: { pages: coordinatesMock } } }); 32 | const page = new Page(pageJson); 33 | const result = await page.coordinates({redirect: true}); 34 | expect(requestMock).toHaveBeenCalledTimes(1); 35 | expect(result).toStrictEqual(coordinatesResult); 36 | }); 37 | 38 | test('coordinates method on page throws coordinates error if response is empty', async () => { 39 | requestMock.mockImplementation(async () => { return [] }); 40 | const page = new Page(pageJson); 41 | const t = async () => { 42 | await page.coordinates() 43 | }; 44 | expect(t).rejects.toThrowError(coordinatesError); 45 | }); 46 | 47 | test('Throws coordinates error if response is empty', async () => { 48 | requestMock.mockImplementation(async () => { return [] }); 49 | const t = async () => { 50 | await coordinates("Test") 51 | }; 52 | expect(t).rejects.toThrowError(coordinatesError); 53 | }); 54 | 55 | test('Returns with results as coordinatesResult', async () => { 56 | requestMock.mockImplementation(async () => { return { query: { pages: coordinatesMock } } }); 57 | const result = await coordinates("Test", true); 58 | expect(result).toStrictEqual(coordinatesResult); 59 | }); 60 | 61 | test('Returns with results as null if coordinates not present', async () => { 62 | requestMock.mockImplementation(async () => { return { query: { pages: {500:{}} } } }); 63 | const result = await coordinates("Test", true); 64 | expect(result).toStrictEqual(null); 65 | }); 66 | 67 | 68 | test('coordinate method on index throws coordinates error if response is empty', async () => { 69 | requestMock.mockImplementation(async () => { return [] }); 70 | const t = async () => { 71 | await wiki.coordinates("Test") 72 | }; 73 | expect(t).rejects.toThrowError(coordinatesError); 74 | }); 75 | 76 | test('coordinates method on index returns a coordinatesResult', async () => { 77 | requestMock.mockImplementation(async () => { return { query: { pages: coordinatesMock } } }); 78 | const result = await wiki.coordinates("Test"); 79 | expect(setTitleMock).toHaveBeenCalledTimes(0); 80 | expect(result).toStrictEqual(coordinatesResult); 81 | }); 82 | 83 | test('coordinates method on index returns a coordinatesResult even when autosuggest is true', async () => { 84 | requestMock.mockImplementation(async () => { return { query: { pages: coordinatesMock } } }); 85 | setTitleMock.mockImplementation(async () => { return "test" }); 86 | const result = await wiki.coordinates("Test", { autoSuggest: true }); 87 | expect(setTitleMock).toHaveBeenCalledTimes(1); 88 | expect(result).toStrictEqual(coordinatesResult); 89 | }); -------------------------------------------------------------------------------- /test/featuredContent.test.ts: -------------------------------------------------------------------------------- 1 | import { fcError } from '../source/errors'; 2 | import * as request from '../source/request'; 3 | import wiki from "../source/index"; 4 | import { fcData } from './samples'; 5 | const requestMock = jest.spyOn(request, "makeRestRequest"); 6 | 7 | const fcMock = fcData; 8 | 9 | afterAll(() => { 10 | requestMock.mockRestore(); 11 | }) 12 | 13 | test('Throws featured content error if response is error', async () => { 14 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 15 | const result = async () => { 16 | await wiki.featuredContent({}) 17 | }; 18 | expect(result).rejects.toThrowError(fcError); 19 | }); 20 | 21 | test('Returns with results as fcResult', async () => { 22 | requestMock.mockImplementation(async () => { return fcMock }); 23 | const result = await wiki.featuredContent(); 24 | expect(result).toStrictEqual(fcMock); 25 | }); 26 | 27 | test('Featured content method call with params passed and no padding', async () => { 28 | requestMock.mockImplementation(async () => { return fcMock }); 29 | const result = await wiki.featuredContent({ year: '2020', month: '1', day: '1' }); 30 | expect(result).toStrictEqual(fcMock); 31 | expect(requestMock).toBeCalledWith(`feed/featured/2020/01/01`, true); 32 | }); 33 | 34 | test('Featured content method call with params passed', async () => { 35 | requestMock.mockImplementation(async () => { return fcMock }); 36 | const result = await wiki.featuredContent({ year: '2020', month: '01', day: '01' }); 37 | expect(result).toStrictEqual(fcMock); 38 | expect(requestMock).toBeCalledWith(`feed/featured/2020/01/01`, true); 39 | }); -------------------------------------------------------------------------------- /test/html.test.ts: -------------------------------------------------------------------------------- 1 | import { htmlError } from '../source/errors'; 2 | import Page, { html } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const htmlMock = { 11 | 500: { revisions: [{ "*": "" }] } 12 | } 13 | 14 | const htmlResult = "" 15 | 16 | afterAll(() => { 17 | requestMock.mockRestore(); 18 | setTitleMock.mockRestore(); 19 | }) 20 | 21 | test('html method on page object returns without calling request if _html field set', async () => { 22 | requestMock.mockImplementation(async () => { return { query: { pages: htmlMock } } }); 23 | const page = new Page(pageJson); 24 | page._html = "" 25 | const result = await page.html(); 26 | expect(requestMock).toHaveBeenCalledTimes(0); 27 | expect(result).toStrictEqual(page._html); 28 | }); 29 | 30 | test('html method on page object returns html', async () => { 31 | requestMock.mockImplementation(async () => { return { query: { pages: htmlMock } } }); 32 | const page = new Page(pageJson); 33 | const result = await page.html({redirect: true}); 34 | expect(requestMock).toHaveBeenCalledTimes(1); 35 | expect(result).toStrictEqual(htmlResult); 36 | }); 37 | 38 | test('html method on page throws html error if response is empty', async () => { 39 | requestMock.mockImplementation(async () => { return [] }); 40 | const page = new Page(pageJson); 41 | const t = async () => { 42 | await page.html(); 43 | }; 44 | expect(t).rejects.toThrowError(htmlError); 45 | }); 46 | 47 | test('Throws html error if response is empty', async () => { 48 | requestMock.mockImplementation(async () => { return [] }); 49 | const t = async () => { 50 | await html("Test") 51 | }; 52 | expect(t).rejects.toThrowError(htmlError); 53 | }); 54 | 55 | test('Returns with results as string', async () => { 56 | requestMock.mockImplementation(async () => { return { query: { pages: htmlMock } } }); 57 | const result = await html("Test"); 58 | expect(result).toStrictEqual(htmlResult); 59 | }); 60 | 61 | test('html method on index throws html error if response is empty', async () => { 62 | requestMock.mockImplementation(async () => { return [] }); 63 | const t = async () => { 64 | await wiki.html("Test"); 65 | }; 66 | expect(t).rejects.toThrowError(htmlError); 67 | }); 68 | 69 | test('html method on index returns a string', async () => { 70 | requestMock.mockImplementation(async () => { return { query: { pages: htmlMock } } }); 71 | const result = await wiki.html("Test"); 72 | expect(setTitleMock).toHaveBeenCalledTimes(0); 73 | expect(result).toStrictEqual(htmlResult); 74 | }); 75 | 76 | test('html method on index returns a string even when autosuggest is true', async () => { 77 | requestMock.mockImplementation(async () => { return { query: { pages: htmlMock } } }); 78 | setTitleMock.mockImplementation(async () => { return "test" }); 79 | const result = await wiki.html("Test", { autoSuggest: true }); 80 | expect(setTitleMock).toHaveBeenCalledTimes(1); 81 | expect(result).toStrictEqual(htmlResult); 82 | }); -------------------------------------------------------------------------------- /test/images.test.ts: -------------------------------------------------------------------------------- 1 | import { imageError } from '../source/errors'; 2 | import Page, { images } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const imageMock = { 11 | 500: { pageid: 500, ns: 0, title: 'test', imagerepository: 'testRepo', imageinfo: [{ url: 'testUrl' }] }, 12 | 501: { pageid: 501, ns: 1, title: 'test', imagerepository: 'testRepo', imageinfo: [{ url: "testUrl" }] } 13 | } 14 | 15 | const imageResult = [{ pageid: 500, ns: 0, title: 'test', imagerepository: 'testRepo', imageinfo: [{ url: 'testUrl' }], url: 'testUrl' }, 16 | { pageid: 501, ns: 1, title: 'test', imagerepository: 'testRepo', imageinfo: [{ url: 'testUrl' }], url: 'testUrl' }] 17 | 18 | afterAll(() => { 19 | requestMock.mockRestore(); 20 | setTitleMock.mockRestore(); 21 | }) 22 | 23 | test('Image method on page object returns without calling request if _images field set', async () => { 24 | requestMock.mockImplementation(async () => { return { query: { pages: imageMock } } }); 25 | const page = new Page(pageJson); 26 | page._images = [] 27 | const result = await page.images(); 28 | expect(requestMock).toHaveBeenCalledTimes(0); 29 | expect(result).toStrictEqual([]); 30 | }); 31 | 32 | test('Image method on page object returns array of images', async () => { 33 | requestMock.mockImplementation(async () => { return { query: { pages: imageMock } } }); 34 | const page = new Page(pageJson); 35 | const result = await page.images(); 36 | expect(requestMock).toHaveBeenCalledTimes(1); 37 | expect(result).toStrictEqual(imageResult); 38 | }); 39 | 40 | test('images method on page throws image error if response is empty', async () => { 41 | requestMock.mockImplementation(async () => { return [] }); 42 | const page = new Page(pageJson); 43 | const t = async () => { 44 | await page.images(); 45 | }; 46 | expect(t).rejects.toThrowError(imageError); 47 | }); 48 | 49 | test('Throws image error if response is empty', async () => { 50 | requestMock.mockImplementation(async () => { return [] }); 51 | const t = async () => { 52 | await images("Test") 53 | }; 54 | expect(t).rejects.toThrowError(imageError); 55 | }); 56 | 57 | test('Returns empty if no images are available', async () => { 58 | requestMock.mockImplementation(async () => { return { query: { pages: {} } } }); 59 | const result = await images("Test"); 60 | expect(result).toStrictEqual([]); 61 | }); 62 | 63 | test('Returns with results an array of imageResult object', async () => { 64 | requestMock.mockImplementation(async () => { return { query: { pages: imageMock } } }); 65 | const result = await images("Test"); 66 | expect(result).toStrictEqual(imageResult); 67 | }); 68 | 69 | test('image method on index throws image error if response is empty', async () => { 70 | requestMock.mockImplementation(async () => { return [] }); 71 | const t = async () => { 72 | await wiki.images("Test") 73 | }; 74 | expect(t).rejects.toThrowError(imageError); 75 | }); 76 | 77 | 78 | test('Image method on index returns array of images', async () => { 79 | requestMock.mockImplementation(async () => { return { query: { pages: imageMock } } }); 80 | const result = await wiki.images("Test"); 81 | expect(setTitleMock).toHaveBeenCalledTimes(0); 82 | expect(result).toStrictEqual(imageResult); 83 | }); 84 | 85 | test('Image method on index returns array of images even when autosuggest is true', async () => { 86 | requestMock.mockImplementation(async () => { return { query: { pages: imageMock } } }); 87 | setTitleMock.mockImplementation(async () => { return "test" }); 88 | const result = await wiki.images("Test", { autoSuggest: true }); 89 | expect(setTitleMock).toHaveBeenCalledTimes(1); 90 | expect(result).toStrictEqual(imageResult); 91 | }); -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { searchError, pageError, geoSearchError, wikiError, autocompletionsError } from '../source/errors'; 2 | import * as request from '../source/request'; 3 | import wiki from "../source/index"; 4 | import Page from '../source/page'; 5 | import * as utils from '../source/utils'; 6 | const requestMock = jest.spyOn(request, "default"); 7 | const restRequestMock = jest.spyOn(request, "makeRestRequest"); 8 | import { pageJson, summaryJson } from './samples'; 9 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 10 | 11 | const searchMock = { 12 | search: ["search1", "search2"], 13 | searchinfo: { suggestion: "suggest" } 14 | } 15 | 16 | const searchResult = { 17 | results: ["search1", "search2"], 18 | suggestion: "suggest" 19 | } 20 | 21 | const imageMock = { 22 | 500: { pageid: 500, ns: 0, title: 'test', imagerepository: 'testRepo', imageinfo: [{ url: 'testUrl' }] }, 23 | 501: { pageid: 501, ns: 1, title: 'test', imagerepository: 'testRepo', imageinfo: [{ url: "testUrl" }] } 24 | } 25 | 26 | const imageResult = [{ pageid: 500, ns: 0, title: 'test', imagerepository: 'testRepo', imageinfo: [{ url: 'testUrl' }], url: 'testUrl' }, 27 | { pageid: 501, ns: 1, title: 'test', imagerepository: 'testRepo', imageinfo: [{ url: 'testUrl' }], url: 'testUrl' }] 28 | 29 | const autocompletionsMock = ["Test", ["Test", "Testosterone", "Testicle", "Test cricket", "Test-driven development"]] 30 | 31 | const pageObject = new Page(pageJson); 32 | 33 | afterAll(() => { 34 | requestMock.mockRestore(); 35 | setTitleMock.mockRestore(); 36 | restRequestMock.mockRestore(); 37 | }) 38 | 39 | test('Default wiki returns results as Page Class', async () => { 40 | requestMock.mockImplementation(async () => { return { query: { pages: { 500: pageJson } } } }); 41 | const result = await wiki("Test"); 42 | expect(result.toString()).toStrictEqual(pageObject.toString()); 43 | }); 44 | 45 | test('Throws search error if some error occurs', async () => { 46 | requestMock.mockImplementation(async () => { return { searchMock } }); 47 | const t = async () => { 48 | await wiki.search("Test") 49 | }; 50 | expect(t).rejects.toThrowError(searchError); 51 | }); 52 | 53 | test('Search returns results as wikiSearchResult', async () => { 54 | requestMock.mockImplementation(async () => { return { query: searchMock } }); 55 | const result = await wiki.search("Test", {suggestion: true}); 56 | expect(result).toStrictEqual(searchResult); 57 | }); 58 | 59 | test('Search returns results as wikiSearchResult with suggestions as null', async () => { 60 | requestMock.mockImplementation(async () => { 61 | return { 62 | query: { 63 | search: ["search1", "search2"] 64 | } 65 | } 66 | }); 67 | const result = await wiki.search("Test", {suggestion: false}); 68 | expect(result).toStrictEqual({ 69 | results: ["search1", "search2"], 70 | suggestion: null 71 | }); 72 | }); 73 | 74 | test('Autocompletions returns results as array of strings', async () => { 75 | requestMock.mockImplementation(async () => { return autocompletionsMock }); 76 | const result = await wiki.autocompletions("Test"); 77 | expect(result).toStrictEqual( 78 | ["Test", "Testosterone", "Testicle", "Test cricket", "Test-driven development"] 79 | ); 80 | }); 81 | 82 | test('Throws autocompletions error if some error occurs', async () => { 83 | requestMock.mockImplementation(async () => { return { autocompletionsMock } }); 84 | const t = async () => { 85 | await wiki.autocompletions("Test") 86 | }; 87 | expect(t).rejects.toThrowError(autocompletionsError); 88 | }); 89 | 90 | test('Throws page error if result doesnt have page', async () => { 91 | requestMock.mockImplementation(async () => { return { } }); 92 | const t = async () => { 93 | await wiki.page("Test") 94 | }; 95 | expect(t).rejects.toThrowError(pageError); 96 | }); 97 | 98 | test('Page throws error if missing attribute present in page', async () => { 99 | requestMock.mockImplementation(async () => { return { query: { pages: { 500: { missing: '' } } } } }); 100 | const t = async () => { 101 | await wiki.page("Test") 102 | }; 103 | expect(t).rejects.toThrowError(pageError); 104 | }); 105 | 106 | test('Page returns results as Page Class', async () => { 107 | requestMock.mockImplementation(async () => { return { query: { pages: { 500: pageJson } } } }); 108 | const result = await wiki.page("Test"); 109 | expect(result.toString()).toStrictEqual(pageObject.toString()); 110 | }); 111 | 112 | test('Page returns results as Page Class with auto suggest set to true', async () => { 113 | requestMock.mockImplementation(async () => { return { query: { pages: { 500: pageJson } } } }); 114 | setTitleMock.mockImplementation(async () => { return "test" }); 115 | const result = await wiki.page("Test", {autoSuggest: true}); 116 | expect(result.toString()).toStrictEqual(pageObject.toString()); 117 | }); 118 | 119 | test('Page returns results as Page Class and loads default fields when preload set to true', async () => { 120 | requestMock.mockImplementationOnce(async () => { return { query: { pages: { 500: pageJson } } } }) 121 | .mockImplementationOnce(async () => { return { query: { pages: imageMock } } }); 122 | restRequestMock.mockImplementation(async () => { return summaryJson }) 123 | const result = await wiki.page("Test", { preload: true, autoSuggest: false }); 124 | expect(result.toString()).toStrictEqual(pageObject.toString()); 125 | expect(result._summary).toStrictEqual(summaryJson); 126 | expect(result._images).toStrictEqual(imageResult); 127 | }); 128 | 129 | test('Page returns results as Page Class and loads fields present in fields when preload set to true', async () => { 130 | requestMock.mockImplementation(async () => { return { query: { pages: { 500: pageJson } } } }); 131 | restRequestMock.mockImplementation(async () => { return summaryJson }); 132 | const result = await wiki.page("Test", { preload: true, fields: ["summary"] }); 133 | expect(result.toString()).toStrictEqual(pageObject.toString()); 134 | expect(result._summary).toStrictEqual(summaryJson); 135 | expect(result._images).toBeFalsy(); 136 | }); 137 | 138 | test('Page returns results as Page Class and loads fields present in fields when preload set to true', async () => { 139 | requestMock.mockImplementation(async () => { return { query: { pages: { 500: pageJson } } } }); 140 | restRequestMock.mockImplementation(async () => { return summaryJson }); 141 | const t = async () => { 142 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 143 | // @ts-ignore: Type error 144 | await wiki.page("Test", { preload: true, fields: ["errorMethod"] }) 145 | }; 146 | expect(t).rejects.toThrowError(pageError); 147 | }); 148 | 149 | test('Languages method throws wikiError if error', async () => { 150 | requestMock.mockImplementation(async () => { throw new Error("error")}); 151 | const t = async () => { 152 | await wiki.languages(); 153 | }; 154 | expect(t).rejects.toThrowError(wikiError); 155 | }); 156 | 157 | test('Languages method returns array of languageResult', async () => { 158 | requestMock.mockImplementation(async () => { return { query: { languages: [{ code: "test1", "*": "" }, { code: "test2", "*": "" }] } } }); 159 | const result = await wiki.languages(); 160 | expect(result).toStrictEqual([{ "test1": "" }, { "test2": "" }]); 161 | }); 162 | 163 | test('Set language returns api url with language set', () => { 164 | const result = wiki.setLang("mal"); 165 | expect(result).toStrictEqual("https://mal.wikipedia.org/w/api.php?"); 166 | }); 167 | 168 | test('Geo search error is thrown in case of error', async () => { 169 | requestMock.mockImplementation(async () => { return {} }); 170 | const t = async () => { 171 | await wiki.geoSearch(2.088, 4.023) 172 | }; 173 | expect(t).rejects.toThrowError(geoSearchError); 174 | }); 175 | 176 | test('geoSearch returns results as geoSearchResult', async () => { 177 | requestMock.mockImplementation(async () => { return { query: { geosearch: [] } } }); 178 | const result = await wiki.geoSearch(2.088, 4.023); 179 | expect(result).toStrictEqual([]); 180 | }); 181 | 182 | test('geoSearch returns results as geoSearchResult with options', async () => { 183 | requestMock.mockImplementation(async () => { return { query: { geosearch: [] } } }); 184 | const result = await wiki.geoSearch(2.088, 4.023, { radius: 5000, limit: 20 }); 185 | expect(requestMock).toHaveBeenCalledWith( 186 | { 187 | 'list': 'geosearch', 188 | 'gsradius': 5000, 189 | 'gscoord': `${2.088}|${4.023}`, 190 | 'gslimit': 20, 191 | 'gsprop': 'type' 192 | } 193 | ); 194 | expect(result).toStrictEqual([]); 195 | }); 196 | 197 | test('Search error is thrown in case of error in suggest', async () => { 198 | requestMock.mockImplementation(async () => { throw new Error("Error") }); 199 | const t = async () => { 200 | await wiki.suggest("Test") 201 | }; 202 | expect(t).rejects.toThrowError(searchError); 203 | }); 204 | 205 | test('Suggest returns null if suggestion not present', async () => { 206 | requestMock.mockImplementation(async () => { return { query: { searchinfo: {} } } }); 207 | const result = await wiki.suggest("test"); 208 | expect(result).toStrictEqual(null); 209 | }); 210 | 211 | test('Suggest returns string', async () => { 212 | requestMock.mockImplementation(async () => { return { query: { searchinfo: {suggestion: "suggest"} } } }); 213 | const result = await wiki.suggest("test"); 214 | expect(result).toStrictEqual("suggest"); 215 | }); 216 | 217 | test('sets the user agent', async () => { 218 | let result = wiki.setUserAgent("testUser"); 219 | expect(result).toStrictEqual(undefined); 220 | }); -------------------------------------------------------------------------------- /test/infobox.test.ts: -------------------------------------------------------------------------------- 1 | import { infoboxError } from '../source/errors'; 2 | import Page, { infobox, rawInfo } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson, rawJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const rawResult= rawJson['*']; 11 | const rawMock = { 500: { revisions: [rawJson] } }; 12 | 13 | const infoboxResult = { 14 | name: 'Linus Torvalds', 15 | image: 'LinuxCon Europe Linus Torvalds 03 (cropped).jpg', 16 | caption: 'Torvalds at LinuxCon Europe 2014' 17 | } 18 | 19 | afterAll(() => { 20 | requestMock.mockRestore(); 21 | setTitleMock.mockRestore(); 22 | }) 23 | 24 | test('infobox method on page object returns without calling request if _infobox field set', async () => { 25 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 26 | const page = new Page(pageJson); 27 | page._infobox = infoboxResult; 28 | const result = await page.infobox(); 29 | expect(requestMock).toHaveBeenCalledTimes(0); 30 | expect(result).toStrictEqual(page._infobox); 31 | }); 32 | 33 | test('Infobox method on page object returns infobox', async () => { 34 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 35 | const page = new Page(pageJson); 36 | const result = await page.infobox({redirect: true}); 37 | expect(requestMock).toHaveBeenCalledTimes(1); 38 | expect(result).toStrictEqual(infoboxResult); 39 | }); 40 | 41 | test('infobox method on page throws infobox error if response is error', async () => { 42 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 43 | const page = new Page(pageJson); 44 | const t = async () => { 45 | await page.infobox(); 46 | }; 47 | expect(t).rejects.toThrowError(infoboxError); 48 | }); 49 | 50 | test('Throws infobox error if response is error', async () => { 51 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 52 | const t = async () => { 53 | await infobox("Test") 54 | }; 55 | expect(t).rejects.toThrowError(infoboxError); 56 | }); 57 | 58 | test('Returns with results as any', async () => { 59 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 60 | const result = await infobox("Test", true); 61 | expect(result).toStrictEqual(infoboxResult); 62 | }); 63 | 64 | test('infobox method on index throws infobox error if response is error', async () => { 65 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 66 | const t = async () => { 67 | await wiki.infobox("Test") 68 | }; 69 | expect(t).rejects.toThrowError(infoboxError); 70 | }); 71 | 72 | test('infobox method on index returns infobox', async () => { 73 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 74 | const result = await wiki.infobox("Test"); 75 | expect(setTitleMock).toHaveBeenCalledTimes(0); 76 | expect(result).toStrictEqual(infoboxResult); 77 | }); 78 | 79 | test('infobox method on index returns a infobox even when autosuggest is true', async () => { 80 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 81 | setTitleMock.mockImplementation(async () => { return "test" }); 82 | const result = await wiki.infobox("Test", { autoSuggest: true }); 83 | expect(setTitleMock).toHaveBeenCalledTimes(1); 84 | expect(result).toStrictEqual(infoboxResult); 85 | }); 86 | 87 | test('rawinfo method returns a string', async () => { 88 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 89 | const result = await rawInfo("Test", {}); 90 | expect(result).toStrictEqual(rawResult); 91 | }); 92 | 93 | test('rawinfo method returns empty if * field is not present', async () => { 94 | requestMock.mockImplementation(async () => { return { query: { pages: { 500: { revisions: {} } } } } }); 95 | const result = await rawInfo("Test", {}); 96 | expect(result).toStrictEqual(''); 97 | }); 98 | 99 | test('rawinfo method throws an error', async () => { 100 | requestMock.mockImplementation(async () => { return {} }); 101 | const t = async () => { 102 | await rawInfo("Test", {}) 103 | }; 104 | expect(t).rejects.toThrowError(infoboxError); 105 | }); -------------------------------------------------------------------------------- /test/intro.test.ts: -------------------------------------------------------------------------------- 1 | import { introError } from '../source/errors'; 2 | import Page, { intro } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const introMock = { 11 | 500: { extract: "This is a test intro" } 12 | } 13 | 14 | const introResult = "This is a test intro" 15 | 16 | afterAll(() => { 17 | requestMock.mockRestore(); 18 | setTitleMock.mockRestore(); 19 | }) 20 | 21 | test('Intro method on page object returns without calling request if _intro field set', async () => { 22 | requestMock.mockImplementation(async () => { return { query: { pages: introMock } } }); 23 | const page = new Page(pageJson); 24 | page._intro = "test intro" 25 | const result = await page.intro(); 26 | expect(requestMock).toHaveBeenCalledTimes(0); 27 | expect(result).toStrictEqual(page._intro); 28 | }); 29 | 30 | test('Intro method on page object returns intro', async () => { 31 | requestMock.mockImplementation(async () => { return { query: { pages: introMock } } }); 32 | const page = new Page(pageJson); 33 | const result = await page.intro({redirect: true}); 34 | expect(requestMock).toHaveBeenCalledTimes(1); 35 | expect(result).toStrictEqual(introResult); 36 | }); 37 | 38 | test('intro method on page throws intro error if response is empty', async () => { 39 | requestMock.mockImplementation(async () => { return [] }); 40 | const page = new Page(pageJson); 41 | const t = async () => { 42 | await page.intro(); 43 | }; 44 | expect(t).rejects.toThrowError(introError); 45 | }); 46 | 47 | test('Throws intro error if response is empty', async () => { 48 | requestMock.mockImplementation(async () => { return [] }); 49 | const t = async () => { 50 | await intro("Test") 51 | }; 52 | expect(t).rejects.toThrowError(introError); 53 | }); 54 | 55 | test('Returns with results as string', async () => { 56 | requestMock.mockImplementation(async () => { return { query: { pages: introMock } } }); 57 | const result = await intro("Test"); 58 | expect(result).toStrictEqual(introResult); 59 | }); 60 | 61 | test('intro method on index throws intro error if response is empty', async () => { 62 | requestMock.mockImplementation(async () => { return [] }); 63 | const t = async () => { 64 | await wiki.intro("Test") 65 | }; 66 | expect(t).rejects.toThrowError(introError); 67 | }); 68 | 69 | test('Intro method on index returns a string', async () => { 70 | requestMock.mockImplementation(async () => { return { query: { pages: introMock } } }); 71 | const result = await wiki.intro("Test"); 72 | expect(setTitleMock).toHaveBeenCalledTimes(0); 73 | expect(result).toStrictEqual(introResult); 74 | }); 75 | 76 | test('Intro method on index returns a string even when autosuggest is true', async () => { 77 | requestMock.mockImplementation(async () => { return { query: { pages: introMock } } }); 78 | setTitleMock.mockImplementation(async () => { return "test" }); 79 | const result = await wiki.intro("Test", { autoSuggest: true }); 80 | expect(setTitleMock).toHaveBeenCalledTimes(1); 81 | expect(result).toStrictEqual(introResult); 82 | }); -------------------------------------------------------------------------------- /test/langlinks.test.ts: -------------------------------------------------------------------------------- 1 | import { linksError } from '../source/errors'; 2 | import Page, { langLinks } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const langLinkskMock = { 11 | 500: { langlinks: [{"*":"Link1", lang: "en", url: "url1"}, {"*": "Link2", lang: "fr", url: "url2"}] } 12 | } 13 | 14 | const langLinksResult = [{title:"Link1", lang: "en", url: "url1"}, {title: "Link2", lang: "fr", url: "url2"}] 15 | 16 | afterAll(() => { 17 | requestMock.mockRestore(); 18 | setTitleMock.mockRestore(); 19 | }) 20 | 21 | test('Lang links method on page object returns without calling request if _langlinks field set', async () => { 22 | requestMock.mockImplementation(async () => { return { query: { pages: langLinkskMock } } }); 23 | const page = new Page(pageJson); 24 | page._langLinks = [] 25 | const result = await page.langLinks(); 26 | expect(requestMock).toHaveBeenCalledTimes(0); 27 | expect(result).toStrictEqual(page._langLinks); 28 | }); 29 | 30 | test('lang links method on page object returns array of langLinksResult', async () => { 31 | requestMock.mockImplementation(async () => { return { query: { pages: langLinkskMock } } }); 32 | const page = new Page(pageJson); 33 | const result = await page.langLinks(); 34 | expect(requestMock).toHaveBeenCalledTimes(1); 35 | expect(result).toStrictEqual(langLinksResult); 36 | }); 37 | 38 | test('lang links method on page throws links error if response is empty', async () => { 39 | requestMock.mockImplementation(async () => { return [] }); 40 | const page = new Page(pageJson); 41 | const t = async () => { 42 | await page.langLinks() 43 | }; 44 | expect(t).rejects.toThrowError(linksError); 45 | }); 46 | 47 | test('Throws links error if response is empty', async () => { 48 | requestMock.mockImplementation(async () => { return [] }); 49 | const t = async () => { 50 | await langLinks("Test") 51 | }; 52 | expect(t).rejects.toThrowError(linksError); 53 | }); 54 | 55 | test('Returns empty if no lang links are available', async () => { 56 | requestMock.mockImplementation(async () => { return { query: { pages: {500: {langlinks:[]}} } } }); 57 | const result = await langLinks("Test"); 58 | expect(result).toStrictEqual([]); 59 | }); 60 | 61 | test('Returns empty if lang links object itself is not available', async () => { 62 | requestMock.mockImplementation(async () => { return { query: { pages: {404: {}} } } }); 63 | const result = await langLinks("Test"); 64 | expect(result).toStrictEqual([]); 65 | }); 66 | 67 | test('Returns with results an array of langLinksResult object', async () => { 68 | requestMock.mockImplementation(async () => { return { query: { pages: langLinkskMock } } }); 69 | const result = await langLinks("Test"); 70 | expect(result).toStrictEqual(langLinksResult); 71 | }); 72 | 73 | test('lang links method on index throws links error if response is empty', async () => { 74 | requestMock.mockImplementation(async () => { return [] }); 75 | const t = async () => { 76 | await wiki.langLinks("Test") 77 | }; 78 | expect(t).rejects.toThrowError(linksError); 79 | }); 80 | 81 | test('lang links method on index returns array of langLinksResult object', async () => { 82 | requestMock.mockImplementation(async () => { return { query: { pages: langLinkskMock } } }); 83 | const result = await wiki.langLinks("Test"); 84 | expect(setTitleMock).toHaveBeenCalledTimes(0); 85 | expect(result).toStrictEqual(langLinksResult); 86 | }); 87 | 88 | test('lang links method on index returns array of langLinksResult even when autosuggest is true', async () => { 89 | requestMock.mockImplementation(async () => { return { query: { pages: langLinkskMock } } }); 90 | setTitleMock.mockImplementation(async () => { return "test" }); 91 | const result = await wiki.langLinks("Test", { autoSuggest: true }); 92 | expect(setTitleMock).toHaveBeenCalledTimes(1); 93 | expect(result).toStrictEqual(langLinksResult); 94 | }); -------------------------------------------------------------------------------- /test/links.test.ts: -------------------------------------------------------------------------------- 1 | import { linksError } from '../source/errors'; 2 | import Page, { links } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const linkMock = { 11 | 500: { links: [{title:"Link1"}, {title: "Link2"}] } 12 | } 13 | 14 | const linkResult = ["Link1", "Link2"] 15 | 16 | afterAll(() => { 17 | requestMock.mockRestore(); 18 | setTitleMock.mockRestore(); 19 | }) 20 | 21 | test('Links method on page object returns without calling request if _links field set', async () => { 22 | requestMock.mockImplementation(async () => { return { query: { pages: linkMock } } }); 23 | const page = new Page(pageJson); 24 | page._links = [] 25 | const result = await page.links(); 26 | expect(requestMock).toHaveBeenCalledTimes(0); 27 | expect(result).toStrictEqual(page._links); 28 | }); 29 | 30 | test('link method on page object returns array of strings', async () => { 31 | requestMock.mockImplementation(async () => { return { query: { pages: linkMock } } }); 32 | const page = new Page(pageJson); 33 | const result = await page.links(); 34 | expect(requestMock).toHaveBeenCalledTimes(1); 35 | expect(result).toStrictEqual(linkResult); 36 | }); 37 | 38 | test('links method on page throws links error if response is empty', async () => { 39 | requestMock.mockImplementation(async () => { return [] }); 40 | const page = new Page(pageJson); 41 | const t = async () => { 42 | await page.links() 43 | }; 44 | expect(t).rejects.toThrowError(linksError); 45 | }); 46 | 47 | test('Throws links error if response is empty', async () => { 48 | requestMock.mockImplementation(async () => { return [] }); 49 | const t = async () => { 50 | await links("Test") 51 | }; 52 | expect(t).rejects.toThrowError(linksError); 53 | }); 54 | 55 | test('Returns empty if no links are available', async () => { 56 | requestMock.mockImplementation(async () => { return { query: { pages: {500: {links:[]}} } } }); 57 | const result = await links("Test"); 58 | expect(result).toStrictEqual([]); 59 | }); 60 | 61 | test('Returns with results an array of string', async () => { 62 | requestMock.mockImplementation(async () => { return { query: { pages: linkMock } } }); 63 | const result = await links("Test"); 64 | expect(result).toStrictEqual(linkResult); 65 | }); 66 | 67 | test('links method on page throws links error if response is empty', async () => { 68 | requestMock.mockImplementation(async () => { return [] }); 69 | const t = async () => { 70 | await wiki.links("Test") 71 | }; 72 | expect(t).rejects.toThrowError(linksError); 73 | }); 74 | 75 | test('links method on index returns array of strings', async () => { 76 | requestMock.mockImplementation(async () => { return { query: { pages: linkMock } } }); 77 | const result = await wiki.links("Test"); 78 | expect(setTitleMock).toHaveBeenCalledTimes(0); 79 | expect(result).toStrictEqual(linkResult); 80 | }); 81 | 82 | test('links method on index returns array of strings even when autosuggest is true', async () => { 83 | requestMock.mockImplementation(async () => { return { query: { pages: linkMock } } }); 84 | setTitleMock.mockImplementation(async () => { return "test" }); 85 | const result = await wiki.links("Test", { autoSuggest: true }); 86 | expect(setTitleMock).toHaveBeenCalledTimes(1); 87 | expect(result).toStrictEqual(linkResult); 88 | }); -------------------------------------------------------------------------------- /test/media.test.ts: -------------------------------------------------------------------------------- 1 | import { mediaError } from '../source/errors'; 2 | import Page, { media } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson, mediaJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "makeRestRequest"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | afterAll(() => { 11 | requestMock.mockRestore(); 12 | setTitleMock.mockRestore(); 13 | }) 14 | 15 | test('media method on page object returns without calling request if _media field set', async () => { 16 | requestMock.mockImplementation(async () => { return mediaJson }); 17 | const page = new Page(pageJson); 18 | page._media = mediaJson; 19 | const result = await page.media(); 20 | expect(requestMock).toHaveBeenCalledTimes(0); 21 | expect(result).toStrictEqual(page._media); 22 | }); 23 | 24 | test('media method on page object returns wikiMediaResult with array of mediaResults', async () => { 25 | requestMock.mockImplementation(async () => { return mediaJson }); 26 | const page = new Page(pageJson); 27 | const result = await page.media({redirect: true}); 28 | expect(requestMock).toHaveBeenCalledTimes(1); 29 | expect(result).toStrictEqual(mediaJson); 30 | }); 31 | 32 | test('media method on page throws media error if response is error', async () => { 33 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 34 | const page = new Page(pageJson); 35 | const t = async () => { 36 | await page.media() 37 | }; 38 | expect(t).rejects.toThrowError(mediaError); 39 | }); 40 | 41 | test('Throws media error if response is error', async () => { 42 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 43 | const t = async () => { 44 | await media("Test") 45 | }; 46 | expect(t).rejects.toThrowError(mediaError); 47 | }); 48 | 49 | test('Returns with results as wikiMediaResult', async () => { 50 | requestMock.mockImplementation(async () => { return mediaJson }); 51 | const result = await media("Test"); 52 | expect(result).toStrictEqual(mediaJson); 53 | }); 54 | 55 | test('media method on index throws media error if response is error', async () => { 56 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 57 | const t = async () => { 58 | await wiki.media("Test") 59 | }; 60 | expect(t).rejects.toThrowError(mediaError); 61 | }); 62 | 63 | test('related method on index returns wikiMediaResult', async () => { 64 | requestMock.mockImplementation(async () => { return mediaJson }); 65 | const result = await wiki.media("Test"); 66 | expect(setTitleMock).toHaveBeenCalledTimes(0); 67 | expect(result).toStrictEqual(mediaJson); 68 | }); 69 | 70 | test('media method on index returns wikiMediaResult even when autosuggest is true', async () => { 71 | requestMock.mockImplementation(async () => { return mediaJson }); 72 | setTitleMock.mockImplementation(async () => { return "test" }); 73 | const result = await wiki.media("Test", { autoSuggest: true }); 74 | expect(setTitleMock).toHaveBeenCalledTimes(1); 75 | expect(result).toStrictEqual(mediaJson); 76 | }); -------------------------------------------------------------------------------- /test/mobileHtml.test.ts: -------------------------------------------------------------------------------- 1 | import * as request from '../source/request'; 2 | import wiki from "../source/index"; 3 | import { htmlString, notFoundJson } from './samples'; 4 | import { htmlError } from '../source/errors'; 5 | import Page, { mobileHtml } from '../source/page'; 6 | import { pageJson } from './samples'; 7 | import * as utils from '../source/utils' 8 | const requestMock = jest.spyOn(request, "makeRestRequest"); 9 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 10 | 11 | const htmlResult = "" 12 | 13 | afterAll(() => { 14 | requestMock.mockRestore(); 15 | setTitleMock.mockRestore(); 16 | }); 17 | 18 | test('mobilehtml method on page object returns without calling request if _mobileHtml field set', async () => { 19 | requestMock.mockImplementation(async () => { return htmlResult }); 20 | const page = new Page(pageJson); 21 | page._mobileHtml = htmlResult; 22 | const result = await page.mobileHtml(); 23 | expect(requestMock).toHaveBeenCalledTimes(0); 24 | expect(result).toStrictEqual(page._mobileHtml); 25 | }); 26 | 27 | test('mobilehtml method on page object returns html', async () => { 28 | requestMock.mockImplementation(async () => { return htmlResult }); 29 | const page = new Page(pageJson); 30 | const result = await page.mobileHtml({redirect: true}); 31 | expect(requestMock).toHaveBeenCalledTimes(1); 32 | expect(result).toStrictEqual(htmlResult); 33 | }); 34 | 35 | test('mobilehtml method on page throws html error if response is empty', async () => { 36 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 37 | const page = new Page(pageJson); 38 | const t = async () => { 39 | await page.mobileHtml(); 40 | }; 41 | expect(t).rejects.toThrowError(htmlError); 42 | }); 43 | 44 | test('Throws mobilehtml error if response is empty', async () => { 45 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 46 | const t = async () => { 47 | await mobileHtml("Test") 48 | }; 49 | expect(t).rejects.toThrowError(htmlError); 50 | }); 51 | 52 | test('Returns with results as string', async () => { 53 | requestMock.mockImplementation(async () => { return htmlResult }); 54 | const result = await mobileHtml("Test"); 55 | expect(result).toStrictEqual(htmlResult); 56 | }); 57 | 58 | test('Returns mobilehtml when page successfully queried', async () => { 59 | requestMock.mockImplementation(async () => { return htmlString }); 60 | const result = await wiki.mobileHtml("ACID"); 61 | expect(result).toStrictEqual(htmlString); 62 | }); 63 | 64 | test('throws htmlError if error response', async () => { 65 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 66 | const t = async () => { 67 | await wiki.mobileHtml("ACID"); 68 | }; 69 | expect(t).rejects.toThrowError(htmlError); 70 | }); 71 | 72 | test('Returns notFound if page not found', async () => { 73 | requestMock.mockImplementation(async () => { return notFoundJson }); 74 | const result = await wiki.mobileHtml("does-not-exist-on-wikipedia"); 75 | expect(result).toEqual(notFoundJson); 76 | }); 77 | 78 | test('Returns empty body on redirect page with redirect set to false', async () => { 79 | requestMock.mockImplementation(async () => { return null }); 80 | setTitleMock.mockImplementation(async () => { return "homestar" }); 81 | const result = await wiki.mobileHtml("homestar", { autoSuggest: true, redirect: false }); 82 | expect(setTitleMock).toHaveBeenCalledTimes(1); 83 | expect(result).toBeNull(); 84 | }); 85 | -------------------------------------------------------------------------------- /test/onThisDay.test.ts: -------------------------------------------------------------------------------- 1 | import { eventsError } from '../source/errors'; 2 | import * as request from '../source/request'; 3 | import wiki from "../source/index"; 4 | import { eventsJson } from './samples'; 5 | const requestMock = jest.spyOn(request, "makeRestRequest"); 6 | 7 | const eventsMock = eventsJson; 8 | 9 | afterAll(() => { 10 | requestMock.mockRestore(); 11 | }) 12 | 13 | test('Throws events error if response is error', async () => { 14 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 15 | const t = async () => { 16 | await wiki.onThisDay({}) 17 | }; 18 | expect(t).rejects.toThrowError(eventsError); 19 | }); 20 | 21 | test('Returns with results as eventResult', async () => { 22 | requestMock.mockImplementation(async () => { return eventsMock }); 23 | const result = await wiki.onThisDay(); 24 | expect(result).toStrictEqual(eventsMock); 25 | }); 26 | 27 | test('events method call with params passed and no padding', async () => { 28 | requestMock.mockImplementation(async () => { return eventsMock }); 29 | const result = await wiki.onThisDay({ type: 'deaths', month: '6', day: '9' }); 30 | expect(result).toStrictEqual(eventsMock); 31 | expect(requestMock).toBeCalledWith(`feed/onthisday/deaths/06/09`, true); 32 | }); 33 | 34 | test('events method call with params passed', async () => { 35 | requestMock.mockImplementation(async () => { return eventsMock }); 36 | const result = await wiki.onThisDay({ type: 'deaths', month: '11', day: '25' }); 37 | expect(result).toStrictEqual(eventsMock); 38 | expect(requestMock).toBeCalledWith(`feed/onthisday/deaths/11/25`, true); 39 | }); -------------------------------------------------------------------------------- /test/pdf.test.ts: -------------------------------------------------------------------------------- 1 | import * as request from '../source/request'; 2 | import wiki from "../source/index"; 3 | import { htmlString, notFoundJson } from './samples'; 4 | import { pdfError } from '../source/errors'; 5 | import Page, { pdf } from '../source/page'; 6 | import { pageJson } from './samples'; 7 | import * as utils from '../source/utils' 8 | const requestMock = jest.spyOn(request, "returnRestUrl"); 9 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 10 | 11 | const pdfResult = "link/pdf" 12 | 13 | afterAll(() => { 14 | requestMock.mockRestore(); 15 | setTitleMock.mockRestore(); 16 | }); 17 | 18 | test('returns link of pdf on page', async () => { 19 | requestMock.mockImplementation(() => { return pdfResult }); 20 | const page = new Page(pageJson); 21 | const result = await page.pdf(); 22 | expect(requestMock).toHaveBeenCalledTimes(1); 23 | expect(result).toStrictEqual(pdfResult); 24 | }); 25 | 26 | test('throws pdf error on page', async () => { 27 | requestMock.mockImplementation(() => { throw Error("this is an error") }); 28 | const page = new Page(pageJson); 29 | const t = async () => { 30 | await page.pdf(); 31 | }; 32 | expect(t).rejects.toThrowError(pdfError); 33 | }); 34 | 35 | test('Throws pdf error', async () => { 36 | requestMock.mockImplementation(() => { throw new Error("This is an error") }); 37 | const t = async () => { 38 | await pdf("Test") 39 | }; 40 | expect(t).rejects.toThrowError(pdfError); 41 | }); 42 | 43 | test('Returns link of pdf', async () => { 44 | requestMock.mockImplementation(() => { return pdfResult }); 45 | const result = await pdf("Test"); 46 | expect(result).toStrictEqual(pdfResult); 47 | }); 48 | 49 | test('Returns link of pdf directly', async () => { 50 | requestMock.mockImplementation(() => { return pdfResult }); 51 | const result = await wiki.pdf("ACID"); 52 | expect(result).toStrictEqual(pdfResult); 53 | }); 54 | 55 | test('throws pdf error directly', async () => { 56 | requestMock.mockImplementation(() => { throw new Error("This is an error") }); 57 | const t = async () => { 58 | await wiki.pdf("ACID"); 59 | }; 60 | expect(t).rejects.toThrowError(pdfError); 61 | }); 62 | 63 | test('Returns link of pdf directly', async () => { 64 | let pdfResultWithAllFormats = pdfResult + '/legal/mobile'; 65 | requestMock.mockImplementation(() => { return pdfResultWithAllFormats }); 66 | const result = await wiki.pdf("ACID", { autoSuggest: true, type: 'mobile', format: 'legal' }); 67 | expect(result).toStrictEqual(pdfResultWithAllFormats); 68 | }); 69 | -------------------------------------------------------------------------------- /test/random.test.ts: -------------------------------------------------------------------------------- 1 | import * as request from '../source/request'; 2 | import wiki from "../source/index"; 3 | import { mobileSections, summaryJson, title } from './samples'; 4 | const requestMock = jest.spyOn(request, "makeRestRequest"); 5 | 6 | afterAll(() => { 7 | requestMock.mockRestore(); 8 | }) 9 | 10 | test('Throws error if response is error', async () => { 11 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 12 | const err = async () => { await wiki.random("some_arg") }; 13 | expect(err).rejects.toThrowError(Error); 14 | }); 15 | 16 | test('Returns summary of random article summary', async () => { 17 | requestMock.mockImplementation(async () => { return summaryJson }); 18 | const result = await wiki.random("summary"); 19 | expect(result).toStrictEqual(summaryJson); 20 | }); 21 | 22 | test('Returns summary of random article mobile sections', async () => { 23 | requestMock.mockImplementation(async () => { return mobileSections }); 24 | const result = await wiki.random("mobile-sections"); 25 | expect(result).toStrictEqual(mobileSections); 26 | }); 27 | 28 | test('Returns summary of random article title', async () => { 29 | requestMock.mockImplementation(async () => { return title }); 30 | const result = await wiki.random("title"); 31 | expect(result).toStrictEqual(title); 32 | }); 33 | 34 | test('Request includes arg provided to method', async () => { 35 | requestMock.mockImplementation(async () => { return summaryJson }); 36 | const result = await wiki.random("summary"); 37 | expect(result).toStrictEqual(summaryJson); 38 | expect(requestMock).toBeCalledWith(`page/random/summary`); 39 | }); 40 | 41 | test('Calling random with no arguments', async () => { 42 | requestMock.mockImplementation(async () => { return summaryJson }); 43 | const result = await wiki.random(); 44 | expect(result).toStrictEqual(summaryJson); 45 | expect(requestMock).toBeCalledWith(`page/random/summary`); 46 | }); 47 | -------------------------------------------------------------------------------- /test/references.test.ts: -------------------------------------------------------------------------------- 1 | import { linksError } from '../source/errors'; 2 | import Page, { references } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const referenceMock = { 11 | 500: { extlinks: [{"*":"Link1"}, {"*": "Link2"}] } 12 | } 13 | 14 | const referenceResult = ["Link1", "Link2"] 15 | 16 | afterAll(() => { 17 | requestMock.mockRestore(); 18 | setTitleMock.mockRestore(); 19 | }) 20 | 21 | test('References method on page object returns without calling request if _references field set', async () => { 22 | requestMock.mockImplementation(async () => { return { query: { pages: referenceMock } } }); 23 | const page = new Page(pageJson); 24 | page._references = [] 25 | const result = await page.references(); 26 | expect(requestMock).toHaveBeenCalledTimes(0); 27 | expect(result).toStrictEqual(page._references); 28 | }); 29 | 30 | test('References method on page object returns array of strings', async () => { 31 | requestMock.mockImplementation(async () => { return { query: { pages: referenceMock } } }); 32 | const page = new Page(pageJson); 33 | const result = await page.references(); 34 | expect(requestMock).toHaveBeenCalledTimes(1); 35 | expect(result).toStrictEqual(referenceResult); 36 | }); 37 | 38 | test('References method on page throws links error if response is empty', async () => { 39 | requestMock.mockImplementation(async () => { return [] }); 40 | const page = new Page(pageJson); 41 | const t = async () => { 42 | await page.references() 43 | }; 44 | expect(t).rejects.toThrowError(linksError); 45 | }); 46 | 47 | test('Throws links error if response is empty', async () => { 48 | requestMock.mockImplementation(async () => { return [] }); 49 | const t = async () => { 50 | await references("Test") 51 | }; 52 | expect(t).rejects.toThrowError(linksError); 53 | }); 54 | 55 | test('Returns empty if no external links are available', async () => { 56 | requestMock.mockImplementation(async () => { return { query: { pages: {500: {extlinks:[]}} } } }); 57 | const result = await references("Test"); 58 | expect(result).toStrictEqual([]); 59 | }); 60 | 61 | test('Returns with results an array of string', async () => { 62 | requestMock.mockImplementation(async () => { return { query: { pages: referenceMock } } }); 63 | const result = await references("Test"); 64 | expect(result).toStrictEqual(referenceResult); 65 | }); 66 | 67 | test('references method on page throws links error if response is empty', async () => { 68 | requestMock.mockImplementation(async () => { return [] }); 69 | const t = async () => { 70 | await wiki.references("Test") 71 | }; 72 | expect(t).rejects.toThrowError(linksError); 73 | }); 74 | 75 | 76 | test('references method on index returns array of strings', async () => { 77 | requestMock.mockImplementation(async () => { return { query: { pages: referenceMock } } }); 78 | const result = await wiki.references("Test"); 79 | expect(setTitleMock).toHaveBeenCalledTimes(0); 80 | expect(result).toStrictEqual(referenceResult); 81 | }); 82 | 83 | test('references method on index returns array of strings even when autosuggest is true', async () => { 84 | requestMock.mockImplementation(async () => { return { query: { pages: referenceMock } } }); 85 | setTitleMock.mockImplementation(async () => { return "test" }); 86 | const result = await wiki.references("Test", { autoSuggest: true }); 87 | expect(setTitleMock).toHaveBeenCalledTimes(1); 88 | expect(result).toStrictEqual(referenceResult); 89 | }); -------------------------------------------------------------------------------- /test/related.test.ts: -------------------------------------------------------------------------------- 1 | import { relatedError } from '../source/errors'; 2 | import Page, { related } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson, summaryJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "makeRestRequest"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const relatedMock = {pages: [summaryJson, summaryJson]}; 11 | 12 | afterAll(() => { 13 | requestMock.mockRestore(); 14 | setTitleMock.mockRestore(); 15 | }) 16 | 17 | test('Related method on page object returns without calling request if _related field set', async () => { 18 | requestMock.mockImplementation(async () => { return relatedMock }); 19 | const page = new Page(pageJson); 20 | page._related = relatedMock; 21 | const result = await page.related(); 22 | expect(requestMock).toHaveBeenCalledTimes(0); 23 | expect(result).toStrictEqual(page._related); 24 | }); 25 | 26 | test('Related method on page object returns array of wikiSummary', async () => { 27 | requestMock.mockImplementation(async () => { return relatedMock }); 28 | const page = new Page(pageJson); 29 | const result = await page.related({redirect: true}); 30 | expect(requestMock).toHaveBeenCalledTimes(1); 31 | expect(result).toStrictEqual(relatedMock); 32 | }); 33 | 34 | test('Related method on page throws related error if response is error', async () => { 35 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 36 | const page = new Page(pageJson); 37 | const t = async () => { 38 | await page.related() 39 | }; 40 | expect(t).rejects.toThrowError(relatedError); 41 | }); 42 | 43 | test('Throws related error if response is error', async () => { 44 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 45 | const t = async () => { 46 | await related("Test") 47 | }; 48 | expect(t).rejects.toThrowError(relatedError); 49 | }); 50 | 51 | test('Returns with results as array of wikiSummary', async () => { 52 | requestMock.mockImplementation(async () => { return relatedMock }); 53 | const result = await related("Test"); 54 | expect(result).toStrictEqual(relatedMock); 55 | }); 56 | 57 | test('related method on index throws related error if response is error', async () => { 58 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 59 | const t = async () => { 60 | await wiki.related("Test") 61 | }; 62 | expect(t).rejects.toThrowError(relatedError); 63 | }); 64 | 65 | test('related method on index returns array of wikiSummary', async () => { 66 | requestMock.mockImplementation(async () => { return relatedMock }); 67 | const result = await wiki.related("Test"); 68 | expect(setTitleMock).toHaveBeenCalledTimes(0); 69 | expect(result).toStrictEqual(relatedMock); 70 | }); 71 | 72 | test('related method on index returns a array of wikiSummary even when autosuggest is true', async () => { 73 | requestMock.mockImplementation(async () => { return relatedMock }); 74 | setTitleMock.mockImplementation(async () => { return "test" }); 75 | const result = await wiki.related("Test", { autoSuggest: true }); 76 | expect(setTitleMock).toHaveBeenCalledTimes(1); 77 | expect(result).toStrictEqual(relatedMock); 78 | }); -------------------------------------------------------------------------------- /test/request.test.ts: -------------------------------------------------------------------------------- 1 | import axios, {AxiosHeaders} from 'axios'; 2 | import {AxiosRequestConfig, AxiosResponse} from 'axios'; 3 | import request, {makeRestRequest, setAPIUrl, returnRestUrl, setUserAgent} from '../source/request'; 4 | import { wikiError } from '../source'; 5 | const fetchMock = jest.spyOn(axios, "get"); 6 | 7 | const options: AxiosRequestConfig = { 8 | headers: { 9 | 'Api-User-Agent': 'wikipedia (https://github.com/dopecodez/Wikipedia/)' 10 | } 11 | } 12 | const baseConfig : AxiosResponse['config'] = { headers: new AxiosHeaders()}; 13 | const baseResponse : Omit = { status: 200, statusText: 'Ok', request: {}, headers: {}, config: baseConfig}; 14 | const response1 : AxiosResponse = {...baseResponse, data:{"test1": "test1"}}; 15 | const response2 : AxiosResponse = {...baseResponse, data:{"test2": "test2"}}; 16 | const response3 : AxiosResponse = {...baseResponse, data:{"test3": "test3"}}; 17 | //const response4 : AxiosResponse = {...baseResponse, data:{"test4": "test4"}}; 18 | const apiUrl = "https://en.wikipedia.org/w/api.php?"; 19 | const restApiUrl = 'https://en.wikipedia.org/api/rest_v1/'; 20 | 21 | afterAll(() => { 22 | fetchMock.mockRestore(); 23 | }) 24 | 25 | test('makeRequest method calls and returns with expected params', async () => { 26 | fetchMock.mockImplementation(async () => { return response1 } ); 27 | await request({}, true); 28 | expect(fetchMock).toHaveBeenCalledWith( 29 | apiUrl + 'format=json&redirects=&action=query&origin=*&', 30 | options 31 | ); 32 | }); 33 | 34 | test('makeRequest method calls and returns with expected params when no value passed for redirect', async () => { 35 | fetchMock.mockImplementation(async () => { return response3 } ); 36 | await request({}); 37 | expect(fetchMock).toHaveBeenCalledWith( 38 | apiUrl + 'format=json&redirects=&action=query&origin=*&', 39 | options 40 | ); 41 | }); 42 | 43 | test('makeRequest throws wiki error if error is raised', async () => { 44 | fetchMock.mockImplementation(async () => { throw new Error("Error") }); 45 | const t = async () => { 46 | await request({}, true) 47 | }; 48 | expect(t).rejects.toThrowError(wikiError); 49 | }); 50 | 51 | test('makeRestRequest method calls and returns with expected params', async () => { 52 | fetchMock.mockImplementation(async () => { return response2 } ); 53 | await makeRestRequest("path", false); 54 | expect(fetchMock).toHaveBeenCalledWith( 55 | restApiUrl + 'path?redirect=false', 56 | options 57 | ); 58 | }); 59 | 60 | test('makeRestRequest throws wiki error if error is raised', async () => { 61 | fetchMock.mockImplementation(async () => { throw new Error("Error") }); 62 | const t = async () => { 63 | await makeRestRequest("") 64 | }; 65 | expect(t).rejects.toThrowError(wikiError); 66 | }); 67 | 68 | // test('makeRestRequest method calls and returns with expected params when no value for redirect', async () => { 69 | // fetchMock.mockImplementation(async () => { return response4 } ); 70 | // await makeRestRequest("path"); 71 | // expect(fetchMock).toHaveBeenCalledWith( 72 | // restApiUrl + 'path?redirect=true', 73 | // options 74 | // ); 75 | // }); 76 | 77 | test('Return rest url', () => { 78 | const result = returnRestUrl("path/pdf"); 79 | expect(result).toStrictEqual("https://en.wikipedia.org/api/rest_v1/path/pdf"); 80 | }); 81 | 82 | test('Set language returns api url with language set', () => { 83 | const result = setAPIUrl("mal"); 84 | expect(result).toStrictEqual("https://mal.wikipedia.org/w/api.php?"); 85 | }); 86 | 87 | test('Set user agent and use it to call the api', async () => { 88 | setUserAgent("testUser"); 89 | fetchMock.mockImplementation(async () => { return response1 } ); 90 | await request({}, true); 91 | const modifiedOptions : AxiosRequestConfig = { 92 | headers: { 93 | 'Api-User-Agent': 'testUser' 94 | } 95 | } 96 | expect(fetchMock).toHaveBeenCalledWith( 97 | expect.anything(), 98 | modifiedOptions 99 | ); 100 | }); -------------------------------------------------------------------------------- /test/samples.ts: -------------------------------------------------------------------------------- 1 | export const pageJson = { 2 | "pageid": 100, 3 | "ns": 1, 4 | "title": "Test", 5 | "contentmodel": "", 6 | "pagelanguage": "", 7 | "pagelanguagehtmlcode": "", 8 | "pagelanguagedir": "", 9 | "touched": "", 10 | "lastrevid": 0, 11 | "length": 0, 12 | "fullurl": "", 13 | "editurl": "", 14 | "canonicalurl": "" 15 | } 16 | 17 | export const summaryJson = { 18 | type: "", 19 | title: "Test Summary", 20 | displaytitle: "Test", 21 | namespace: { id: 0, text: "" }, 22 | wikibase_item: "", 23 | titles: { canonical: "", normalized: "", display: "" }, 24 | pageid: 100, 25 | thumbnail: { 26 | source: "", 27 | width: 0, 28 | height: 0 29 | }, 30 | originalimage: { 31 | source: "", 32 | width: 0, 33 | height: 0 34 | }, 35 | lang: "", 36 | dir: "", 37 | revision: "", 38 | tid: "", 39 | timestamp: "", 40 | description: "", 41 | description_source: "", 42 | content_urls: { 43 | desktop: { 44 | page: "", 45 | revisions: "", 46 | edit: "", 47 | talk: "" 48 | }, 49 | mobile: { 50 | page: "", 51 | revisions: "", 52 | edit: "", 53 | talk: "" 54 | } 55 | }, 56 | extract: "", 57 | extract_html: "" 58 | } 59 | 60 | export const eventsJson = { 61 | births: [ 62 | { 63 | text: "test", 64 | pages: [summaryJson] 65 | } 66 | ], 67 | deaths: [ 68 | { 69 | text: "test", 70 | pages: [summaryJson] 71 | } 72 | ], 73 | events: [ 74 | { 75 | text: "test", 76 | pages: [summaryJson] 77 | } 78 | ], 79 | holidays: [ 80 | { 81 | text: "test", 82 | pages: [summaryJson] 83 | } 84 | ], 85 | selected: [ 86 | { 87 | text: "test", 88 | pages: [summaryJson] 89 | } 90 | ] 91 | } 92 | 93 | export const rawJson = { 94 | contentformat: 'text/x-wiki', 95 | contentmodel: 'wikitext', 96 | '*': '{{pp-move-indef}}\n' + 97 | '{{short description|Creator and lead developer of Linux kernel}}\n' + 98 | '{{Use dmy dates|date=July 2020}}\n' + 99 | '{{Infobox person\n' + 100 | '| name = Linus Torvalds\n' + 101 | '| image = LinuxCon Europe Linus Torvalds 03 (cropped).jpg\n' + 102 | '| caption = Torvalds at LinuxCon Europe 2014\n' 103 | } 104 | 105 | export const tableJson = { 106 | contentformat: 'text/x-wiki', 107 | contentmodel: 'wikitext', 108 | '*': '{{pp-move-indef}}\n' + 109 | '== Awards and achievements ==\n' + 110 | '{| class="wikitable"\n' + 111 | '|-\n' + 112 | '! colspan="3" style="background: LightSteelBlue;" | Awards and achievements\n' + 113 | '|- style="background:#ccc;"\n' + 114 | '! Year !! Award !! Notes\n' + 115 | '|-\n' + 116 | '|2018\n' + 117 | '|IEEE Masaru Ibuka Consumer Electronics Award\n' + 118 | '|[[IEEE Masaru Ibuka Consumer Electronics Award]] is conferred by the [[Institute of Electrical and Electronics Engineers]] for outstanding contributions to consumer electronics technology has been named in honor the co-founder and honorary chairman of Sony Corporation, Masaru Ibuka. 2018 Ibuka award was conferred to Linus Torvalds "For his leadership of the development and proliferation of Linux."\n' + 119 | '|-\n' + 120 | '|2014\n' + 121 | '|IEEE Computer Pioneer Award\n' + 122 | "|On 23 April 2014, the [[Institute of Electrical and Electronics Engineers]] named Torvalds as the 2014 recipient of the IEEE Computer Society's Computer Pioneer Award. The Computer Pioneer Award was established in 1981 by the IEEE Computer Society Board of Governors to recognize and honor the vision of those whose efforts resulted in the creation and continued vitality of the computer industry. The award is presented to outstanding individuals whose main contribution to the concepts and development of the computer field was made at least 15 years earlier.{{cite web|url=http://www.computer.org/portal/web/pressroom/Linus-Torvalds-Named-Recipient-of-the-2014-IEEE-Computer-Society-Computer-Pioneer-Award|title=Linus Torvalds Named Recipient of the 2014 IEEE Computer Society Computer Pioneer Award|publisher=[[Institute of Electrical and Electronics Engineers]]|date=23 April 2014|accessdate=5 May 2014|archive-url=https://web.archive.org/web/20140504034244/http://www.computer.org/portal/web/pressroom/Linus-Torvalds-Named-Recipient-of-the-2014-IEEE-Computer-Society-Computer-Pioneer-Award|archive-date=4 May 2014|url-status=dead}}\n" + 123 | '|-\n' + 124 | '|}\n' + 125 | '\n' + 126 | '== Media recognition ==\n' + 127 | "[[Time (magazine)|''Time'' magazine]] has recognized Torvalds multiple times:\n" 128 | } 129 | 130 | export const mediaJson = { 131 | revision: "111", 132 | tid: "222", 133 | items: [{ 134 | title: "sample", 135 | section_id: 1, 136 | type: "image", 137 | caption: { 138 | html: "test", 139 | text: "test" 140 | }, 141 | showInGallery: true, 142 | srcset: [{ 143 | src: "/something.jpg", 144 | scale: "1" 145 | }] 146 | }] 147 | } 148 | 149 | export const mobileSections = { 150 | lead: { 151 | ns: 0, 152 | id: 3449027, 153 | revision: "1000443218", 154 | lastmodified: "2021-01-15T03:44:53Z", 155 | lastmodifier: { 156 | user: "Ser Amantio di Nicolao", 157 | gender: "unknown" 158 | }, 159 | displaytitle: "Girls Like Me", 160 | normalizedtitle: "Girls Like Me", 161 | wikibase_item: "Q5564650", 162 | description: "1986 studio album by Tanya Tucker", 163 | description_source: "local", 164 | protection: {}, 165 | editable: true, 166 | languagecount: 1, 167 | image: { 168 | file: "TanyaTuckerGirlsLikeMeOriginal.jpg", 169 | urls: { 170 | 320: "https://upload.wikimedia.org/wikipedia/en/4/48/TanyaTuckerGirlsLikeMeOriginal.jpg", 171 | 640: "https://upload.wikimedia.org/wikipedia/en/4/48/TanyaTuckerGirlsLikeMeOriginal.jpg", 172 | 800: "https://upload.wikimedia.org/wikipedia/en/4/48/TanyaTuckerGirlsLikeMeOriginal.jpg", 173 | 1024: "https://upload.wikimedia.org/wikipedia/en/4/48/TanyaTuckerGirlsLikeMeOriginal.jpg" 174 | } 175 | }, 176 | issues: [ 177 | { 178 | html: "This is article", 179 | text: "This is article" 180 | } 181 | ], 182 | sections: [ 183 | { 184 | id: 0, 185 | text: "

This is article

" 186 | }, 187 | { 188 | id: 1, 189 | toclevel: 1, 190 | anchor: "Track_listing", 191 | line: "Track listing" 192 | }, 193 | { 194 | id: 2, 195 | toclevel: 1, 196 | anchor: "Chart_performance", 197 | line: "Chart performance" 198 | } 199 | ] 200 | }, 201 | remaining: { 202 | sections: [ 203 | { 204 | id: 1, 205 | text: "\n
  1. \"One Love at a Time\"
", 206 | toclevel: 1, 207 | line: "Track listing", 208 | anchor: "Track_listing" 209 | }, 210 | { 211 | id: 2, 212 | text: "\n\n
abc
", 213 | toclevel: 1, 214 | line: "Chart performance", 215 | anchor: "Chart_performance" 216 | } 217 | ] 218 | } 219 | } 220 | 221 | export const title = { 222 | items: [ 223 | { 224 | title: "White-naped_seedeater", 225 | page_id: 12450272, 226 | rev: 1012232317, 227 | tid: "fce161c0-8ea1-11eb-8f0f-a75f37ec29b6", 228 | namespace: 0, 229 | user_id: 40600116, 230 | user_text: "ShortDescBot", 231 | timestamp: "2021-03-15T09:14:24Z", 232 | comment: "[[User:ShortDescBot|ShortDescBot]] adding [[Wikipedia:Short description|short description]] \"Species of bird\"", 233 | tags: [], 234 | restrictions: [], 235 | page_language: "en", 236 | redirect: false 237 | } 238 | ] 239 | } 240 | 241 | export const notFoundJson = { 242 | type: "https://mediawiki.org/wiki/HyperSwitch/errors/not_found", 243 | title: "Not found.", 244 | method: "get", 245 | detail: "Page or revision not found.", 246 | uri: "/en.wikipedia.org/v1/page/mobile-html/does-not-exist-on-wikipedia" 247 | } 248 | 249 | export const htmlString = "abcde"; 250 | 251 | export const citationData = { 252 | data: [ 253 | { 254 | "itemType": "journalArticle", 255 | "issue": "2", 256 | "DOI": "10.23884/ijesg.2017.2.2.04", 257 | "pages": "47–63", 258 | "title": "A MODEL APPLICATION OF MICRO GRID: BATMAN UNIVERSITY MICRO GRID", 259 | "volume": "2", 260 | "publicationTitle": "International Journal of Energy and Smart Grid", 261 | "date": "2017-12-30", 262 | "url": "http://dx.doi.org/10.23884/ijesg.2017.2.2.04", 263 | "ISSN": [ 264 | "2548-0332" 265 | ], 266 | "accessDate": "2021-09-23", 267 | "author": [ 268 | [ 269 | "Mehmet Esref", 270 | "Demir" 271 | ], 272 | [ 273 | "Musa", 274 | "Yilmaz" 275 | ], 276 | [ 277 | "Ahmet", 278 | "Gündogdu" 279 | ] 280 | ], 281 | "source": [ 282 | "Crossref" 283 | ] 284 | }, 285 | { 286 | "itemType": "book", 287 | "title": "Batman.", 288 | "url": "http://worldcat.org/oclc/983466999", 289 | "abstractNote": "\"Batman: Arkham Asylum exposes players to a dark and atmospheric adventure that takes them into the depths of Arkham Asylum. Batman Arkham City introduces a brand-new story that draws together an all-star cast of classic characters and murderous villains from the Batman universe\"--Amazon.com.", 290 | "oclc": "983466999", 291 | "author": [ 292 | [ 293 | "WB Games (Firm), contributor. Warner Bros. Interactive Entertainment,", 294 | "publisher." 295 | ] 296 | ], 297 | "accessDate": "2021-09-23", 298 | "source": [ 299 | "WorldCat" 300 | ] 301 | } 302 | ] 303 | } 304 | 305 | const sampleSummary = { 306 | "type": "standard", 307 | "title": "Jared_Lee_Loughner", 308 | "displaytitle": "Jared Lee Loughner", 309 | "namespace": { 310 | "id": 0, 311 | "text": "" 312 | }, 313 | "wikibase_item": "Q4267485", 314 | "titles": { 315 | "canonical": "Jared_Lee_Loughner", 316 | "normalized": "Jared Lee Loughner", 317 | "display": "Jared Lee Loughner" 318 | }, 319 | "pageid": 30372228, 320 | "thumbnail": { 321 | "source": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Jared_Loughner_USMS.jpg/256px-Jared_Loughner_USMS.jpg", 322 | "width": 256, 323 | "height": 320 324 | }, 325 | "originalimage": { 326 | "source": "https://upload.wikimedia.org/wikipedia/commons/8/82/Jared_Loughner_USMS.jpg", 327 | "width": 480, 328 | "height": 600 329 | }, 330 | "lang": "en", 331 | "dir": "ltr", 332 | "revision": "1063449101", 333 | "tid": "fa00d540-6d07-11ec-b627-0543a4912f8c", 334 | "timestamp": "2022-01-03T04:11:55Z", 335 | "description": "American mass murderer", 336 | "description_source": "local", 337 | "content_urls": { 338 | "desktop": { 339 | "page": "https://en.wikipedia.org/wiki/Jared_Lee_Loughner", 340 | "revisions": "https://en.wikipedia.org/wiki/Jared_Lee_Loughner?action=history", 341 | "edit": "https://en.wikipedia.org/wiki/Jared_Lee_Loughner?action=edit", 342 | "talk": "https://en.wikipedia.org/wiki/Talk:Jared_Lee_Loughner" 343 | }, 344 | "mobile": { 345 | "page": "https://en.m.wikipedia.org/wiki/Jared_Lee_Loughner", 346 | "revisions": "https://en.m.wikipedia.org/wiki/Special:History/Jared_Lee_Loughner", 347 | "edit": "https://en.m.wikipedia.org/wiki/Jared_Lee_Loughner?action=edit", 348 | "talk": "https://en.m.wikipedia.org/wiki/Talk:Jared_Lee_Loughner" 349 | } 350 | }, 351 | "rxtract": "abc", 352 | "extract_html": "

abc

", 353 | "normalizedtitle": "abc" 354 | } 355 | 356 | export const fcData = { 357 | "tfa": sampleSummary, 358 | "mostread": { 359 | "date": "2022-01-07Z", 360 | "articles": [ 361 | { 362 | "views": 1064940, 363 | "rank": 3, 364 | "view_history": [ 365 | { 366 | "date": "2022-01-07Z", 367 | "views": 1064940 368 | } 369 | ], 370 | ...sampleSummary 371 | }, 372 | ] 373 | }, 374 | "image": { 375 | "title": "File:1930s Japan Travel Poster - 01.jpg", 376 | "thumbnail": { 377 | "source": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/1930s_Japan_Travel_Poster_-_01.jpg/640px-1930s_Japan_Travel_Poster_-_01.jpg", 378 | "width": 640, 379 | "height": 934 380 | }, 381 | "image": { 382 | "source": "https://upload.wikimedia.org/wikipedia/commons/5/54/1930s_Japan_Travel_Poster_-_01.jpg", 383 | "width": 2056, 384 | "height": 3000 385 | }, 386 | "file_page": "https://commons.wikimedia.org/wiki/File:1930s_Japan_Travel_Poster_-_01.jpg", 387 | "artist": { 388 | "html": "Japanese Government Railways", 389 | "text": "Japanese Government Railways", 390 | "name": "Japanese Government Railways" 391 | }, 392 | "credit": { 393 | "html": "Heritage Auctions", 394 | "text": "Heritage Auctions" 395 | }, 396 | "license": { 397 | "type": "Public domain", 398 | "code": "pd" 399 | }, 400 | "description": { 401 | "html": "Sea bathing in Obama", 402 | "text": "Sea bathing in Obama", 403 | "lang": "en" 404 | }, 405 | "wb_entity_id": "M31526367", 406 | "structured": { 407 | "captions": {} 408 | } 409 | }, 410 | "news": [ 411 | { 412 | "links": [ sampleSummary ], 413 | "story": "Bahamian-American actor." 414 | }, 415 | ], 416 | "onthisday": [ 417 | { 418 | "text": "abc", 419 | "pages": [ sampleSummary ] 420 | } 421 | ], 422 | } -------------------------------------------------------------------------------- /test/summary.test.ts: -------------------------------------------------------------------------------- 1 | import { summaryError } from '../source/errors'; 2 | import Page, { summary } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson, summaryJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "makeRestRequest"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const summaryMock = summaryJson; 11 | 12 | afterAll(() => { 13 | requestMock.mockRestore(); 14 | setTitleMock.mockRestore(); 15 | }) 16 | 17 | test('Summary method on page object returns without calling request if _summary field set', async () => { 18 | requestMock.mockImplementation(async () => { return summaryMock }); 19 | const page = new Page(pageJson); 20 | page._summary = summaryMock; 21 | const result = await page.summary(); 22 | expect(requestMock).toHaveBeenCalledTimes(0); 23 | expect(result).toStrictEqual(page._summary); 24 | }); 25 | 26 | test('Summary method on page object returns wikiSummary', async () => { 27 | requestMock.mockImplementation(async () => { return summaryMock }); 28 | const page = new Page(pageJson); 29 | const result = await page.summary({redirect: true}); 30 | expect(requestMock).toHaveBeenCalledTimes(1); 31 | expect(result).toStrictEqual(summaryMock); 32 | }); 33 | 34 | test('summary method on page throws summary error if response is error', async () => { 35 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 36 | const page = new Page(pageJson); 37 | const t = async () => { 38 | await page.summary() 39 | }; 40 | expect(t).rejects.toThrowError(summaryError); 41 | }); 42 | 43 | test('Throws summary error if response is error', async () => { 44 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 45 | const t = async () => { 46 | await summary("Test") 47 | }; 48 | expect(t).rejects.toThrowError(summaryError); 49 | }); 50 | 51 | test('Returns with results as wikiSummary', async () => { 52 | requestMock.mockImplementation(async () => { return summaryMock }); 53 | const result = await summary("Test"); 54 | expect(result).toStrictEqual(summaryMock); 55 | }); 56 | 57 | test('summary method on index throws summary error if response is error', async () => { 58 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 59 | const t = async () => { 60 | await wiki.summary("Test") 61 | }; 62 | expect(t).rejects.toThrowError(summaryError); 63 | }); 64 | 65 | test('summary method on index returns wikiSummary', async () => { 66 | requestMock.mockImplementation(async () => { return summaryMock }); 67 | const result = await wiki.summary("Test"); 68 | expect(setTitleMock).toHaveBeenCalledTimes(0); 69 | expect(result).toStrictEqual(summaryMock); 70 | }); 71 | 72 | test('summary method on index returns a wikiSummary even when autosuggest is true', async () => { 73 | requestMock.mockImplementation(async () => { return summaryMock }); 74 | setTitleMock.mockImplementation(async () => { return "test" }); 75 | const result = await wiki.summary("Test", { autoSuggest: true }); 76 | expect(setTitleMock).toHaveBeenCalledTimes(1); 77 | expect(result).toStrictEqual(summaryMock); 78 | }); -------------------------------------------------------------------------------- /test/tables.test.ts: -------------------------------------------------------------------------------- 1 | import { infoboxError } from '../source/errors'; 2 | import Page, { tables } from '../source/page'; 3 | import * as request from '../source/request'; 4 | import wiki from "../source/index"; 5 | import * as utils from '../source/utils' 6 | import { pageJson, tableJson } from './samples'; 7 | const requestMock = jest.spyOn(request, "default"); 8 | const setTitleMock = jest.spyOn(utils, "setTitleForPage"); 9 | 10 | const rawMock = { 500: { revisions: [tableJson] } }; 11 | 12 | const tablesResult = [[ 13 | { 14 | awardsAndAchievements: 'style="background:#ccc;"\n! Year !! Award !! Notes' 15 | }, 16 | { awardsAndAchievements: '2018' }, 17 | { awardsAndAchievements: '2014' } 18 | ]] 19 | 20 | 21 | afterAll(() => { 22 | requestMock.mockRestore(); 23 | setTitleMock.mockRestore(); 24 | }) 25 | 26 | test('tables method on page object returns without calling request if _tables field set', async () => { 27 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 28 | const page = new Page(pageJson); 29 | page._tables = tablesResult; 30 | const result = await page.tables(); 31 | expect(requestMock).toHaveBeenCalledTimes(0); 32 | expect(result).toStrictEqual(page._tables); 33 | }); 34 | 35 | test('Tables method on page object returns tables as array', async () => { 36 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 37 | const page = new Page(pageJson); 38 | const result = await page.tables({redirect: true}); 39 | expect(requestMock).toHaveBeenCalledTimes(1); 40 | expect(result).toStrictEqual(tablesResult); 41 | }); 42 | 43 | test('Tables method on index throws infobox error if response is error', async () => { 44 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 45 | const page = new Page(pageJson); 46 | const t = async () => { 47 | await page.tables() 48 | }; 49 | expect(t).rejects.toThrowError(infoboxError); 50 | }); 51 | 52 | test('Throws infobox error if response is error', async () => { 53 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 54 | const t = async () => { 55 | await tables("Test") 56 | }; 57 | expect(t).rejects.toThrowError(infoboxError); 58 | }); 59 | 60 | test('Returns with results as array of any', async () => { 61 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 62 | const result = await tables("Test", true); 63 | expect(result).toStrictEqual(tablesResult); 64 | }); 65 | 66 | test('table method on index throws infobox error if response is error', async () => { 67 | requestMock.mockImplementation(async () => { throw new Error("This is an error") }); 68 | const t = async () => { 69 | await wiki.tables("Test") 70 | }; 71 | expect(t).rejects.toThrowError(infoboxError); 72 | }); 73 | 74 | test('tables method on index returns tables as array', async () => { 75 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 76 | const result = await wiki.tables("Test"); 77 | expect(setTitleMock).toHaveBeenCalledTimes(0); 78 | expect(result).toStrictEqual(tablesResult); 79 | }); 80 | 81 | test('tables method on index returns array of tables even when autosuggest is true', async () => { 82 | requestMock.mockImplementation(async () => { return { query: { pages: rawMock } } }); 83 | setTitleMock.mockImplementation(async () => { return "test" }); 84 | const result = await wiki.tables("Test", { autoSuggest: true }); 85 | expect(setTitleMock).toHaveBeenCalledTimes(1); 86 | expect(result).toStrictEqual(tablesResult); 87 | }); -------------------------------------------------------------------------------- /test/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { isString, setTitleForPage, setPageIdOrTitleParam, setPageId } from '../source/utils'; 2 | import wiki from "../source/index"; 3 | import { pageError } from '../source/errors'; 4 | const searchMock = jest.spyOn(wiki, "search"); 5 | 6 | afterAll(() => { 7 | searchMock.mockRestore(); 8 | }) 9 | 10 | test('Is String returns false for numbers', () => { 11 | expect(isString(1)).toBe(false); 12 | }); 13 | 14 | test('Is String returns true for strings', () => { 15 | expect(isString("test")).toBe(true); 16 | }); 17 | 18 | test('Returns error if no suggestion or search results are present', async () => { 19 | searchMock.mockImplementation(async () => { return { suggestion: null, results: [] } }); 20 | const t = async () => { 21 | await setTitleForPage("Test") 22 | }; 23 | expect(t).rejects.toThrowError(pageError); 24 | }); 25 | 26 | test('Returns suggestion if suggestion is present', async () => { 27 | searchMock.mockImplementation(async () => { return { suggestion: "Suggest", results: ['result'] } }); 28 | const result = await setTitleForPage("Test"); 29 | expect(result).toBe("Suggest"); 30 | }); 31 | 32 | test('Returns title if no suggestion but search results are present', async () => { 33 | searchMock.mockImplementation(async () => { return { suggestion: null, results: ['result'] } }); 34 | const result = await setTitleForPage("Test"); 35 | expect(result).toBe("Test"); 36 | }); 37 | 38 | test('Sets title param for string titles', () => { 39 | const params = {} 40 | const result = setPageIdOrTitleParam(params, "Test") 41 | expect(result.titles).toBe("Test"); 42 | }); 43 | 44 | test('Sets page ids params for number titles', () => { 45 | const params = {} 46 | const result = setPageIdOrTitleParam(params, "112") 47 | expect(result.pageids).toBe("112"); 48 | }); 49 | 50 | test('Sets pageId from params if present', () => { 51 | const params = { pageIds: 500 } 52 | const result = setPageId(params, ""); 53 | expect(result).toBe(500); 54 | }); 55 | 56 | test('Sets pageid from result if not present in params', () => { 57 | const output = { query: { pages: { 500: {} } } } 58 | const result = setPageId({}, output); 59 | expect(result).toBe("500"); 60 | }); 61 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es2018", // Node.js 10 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "types" : [ "node", "jest" ], 9 | "strict": true, 10 | "noImplicitReturns": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "allowSyntheticDefaultImports": true, 14 | "module": "commonjs", 15 | "moduleResolution": "node", 16 | "declaration": true, 17 | "useUnknownInCatchVariables": false 18 | }, 19 | "include": [ 20 | "source" 21 | ], 22 | "typedocOptions": { 23 | "exclude": ["**/node_modules/**"], 24 | "mode": "modules", 25 | "out": "docs" 26 | } 27 | } --------------------------------------------------------------------------------