'use strict';
// load module
const Wikiapi = require('../Wikiapi.js');
const CeL = global.CeL;
CeL.info('Using CeJS version: ' + CeL.version);
// load modules for test
CeL.run(['application.debug.log',
// gettext(), and for .detect_HTML_language(), .time_zone_of_language()
'application.locale.gettext'
]);
// ============================================================================
/** {ℕ⁰:Natural+0}count of all errors (failed + fatal) */
let all_error_count = 0;
/** {ℕ⁰:Natural+0}all tests count */
let all_tests = 0;
/** {ℕ⁰:Natural+0}tests done */
let test_done = 0;
/** {ℕ⁰:Natural}test start time value */
const test_start_time = Date.now();
function check_tests(recorder, error_count) {
all_error_count += error_count;
++test_done;
if (test_done < all_tests) {
return;
}
// finish_test
// 耗時,經過時間
const elapsed_message = ' Elapsed time: '
+ Math.round((Date.now() - test_start_time) / 1000) + ' s.';
if (all_error_count === 0) {
CeL.info(`check_tests: All ${all_tests} test group(s) done.${elapsed_message}`);
// normal done. No error.
return;
}
CeL.gettext.conversion['error'] = ['no %n', '1 %n', '%d %ns'];
const error_message = CeL.gettext('All %error@1.', all_error_count) + elapsed_message;
throw new Error(error_message);
}
function add_test(test_name, conditions) {
if (!conditions) {
// shift arguments: 跳過 test_name。
conditions = test_name;
test_name = null;
}
all_tests++;
CeL.test(test_name, conditions, check_tests);
}
// ============================================================================
// Just for test
delete CeL.wiki.query.default_maxlag;
add_test('load page', async (assert, setup_test, finish_test) => {
const enwiki = new Wikiapi('en');
let page_data;
setup_test('load page: [[w:en:Universe]]');
assert(['enwiki', enwiki.site_name()], '.site_name() #1');
assert(['zhwiki', Wikiapi.site_name('zh')], '.site_name() #2');
page_data = await enwiki.page('Universe');
// console.log(CeL.wiki.title_link_of(page_data) + ':');
// console.log(page_data.wikitext);
assert(page_data.wikitext.includes('space]]')
&& page_data.wikitext.includes('time]]'), 'load page: wikitext');
finish_test('load page: [[w:en:Universe]]');
setup_test('load page: [[w:en:Earth]]');
page_data = await enwiki.page('Earth', {
revisions: 2
});
// console.log(CeL.wiki.title_link_of(page_data) + ':');
// console.log(page_data.revisions);
assert([page_data.revisions.length, 2], 'load page: revisions.length');
assert([page_data.wikitext, page_data.revision(0)], 'load page: revision(0)');
assert(page_data.wikitext !== page_data.revision(1), 'load page: revision(1)');
const redirects_taregt = await enwiki.redirects_root('WP:SB');
assert(['Wikipedia:Sandbox', redirects_taregt], '.redirects_root()');
const redirects_list = await enwiki.redirects_here('WP:SB');
assert(['WP:SB', redirects_list.query_title], '.redirects_here() #1');
assert(redirects_list.length > 1, '.redirects_here() #2');
assert(['Wikipedia:Sandbox', redirects_list[0].title], '.redirects_here() #3');
finish_test('load page: [[w:en:Earth]]');
});
// https://en.wikipedia.org/wiki/List_of_wikis
// https://meta.wikimedia.org/wiki/List_of_largest_wikis
add_test('load page of other wiki', async (assert, setup_test, finish_test) => {
const wiki = new Wikiapi('https://harrypotter.fandom.com/api.php');
let page_data;
setup_test('load page of other wiki: [[Harry Potter]]');
page_data = await wiki.page('Harry Potter');
// console.log(page_data.wikitext);
assert(page_data.wikitext.includes('{{Individual infobox'), 'load page: wikitext of [[Harry Potter]]');
finish_test('load page of other wiki: [[Harry Potter]]');
});
// ------------------------------------------------------------------
function normally_blocked_edit(result) {
// @see wiki_API.edit @ wiki.js
return result.edit && result.edit.captcha
// e.g., [[m:NOP|Open Proxy]] is blocked.
|| result.error && (result.error.code === 'globalblocking-ipblocked-range' || result.error.code === 'wikimedia-globalblocking-ipblocked-range');
}
function handle_edit_error(assert, error) {
const result = error.result;
if (normally_blocked_edit(result)) {
CeL.log(`handle_edit_error: Skip blocked edit: ${result.message || result.error && result.error.code || JSON.stringify(result)}`);
return;
}
assert(error.message === '[blocked] You have been blocked from editing.'
|| error.message === 'OK', 'test edit page result');
}
add_test('edit page', async (assert, setup_test, finish_test) => {
setup_test('edit page');
const test_page_title = 'Project:Sandbox';
const test_wikitext = '\nTest edit using {{GitHub|kanasimi/wikiapi}}.';
const bot_name = null;
const password = null;
const enwiki = new Wikiapi;
await enwiki.login({ user_name: bot_name, password, API_URL: 'en' });
const query_result = await enwiki.query({ action: 'query', meta: 'userinfo' });
// node.js v12 does not support the optional chaining operator (?.)
// EoL of node.js v12: 2022-04-30
if (password) {
//assert([bot_name, query_result?.query?.userinfo?.name], 'test wiki.query()');
assert([bot_name, query_result.query.userinfo.name], 'test wiki.query()');
} else {
//assert(['' in query_result?.query?.userinfo?.anon], 'test wiki.query()');
assert(['' in query_result.query.userinfo.anon], 'test wiki.query()');
}
await enwiki.page(test_page_title);
// CeL.set_debug(6);
try {
await enwiki.edit((page_data) => {
// append text
return page_data.wikitext
+ test_wikitext;
}, {
bot: 1,
summary: 'Test edit using wikiapi module'
});
// edit successed
// reget page to test.
const page_data = await enwiki.page(test_page_title);
assert(page_data.wikitext.endsWith(test_wikitext), 'test edit page result');
} catch (error) {
// failed to edit
handle_edit_error(assert, error);
}
// CeL.set_debug(0);
// console.log('Done.');
finish_test('edit page');
});
add_test('edit page #2', async (assert, setup_test, finish_test) => {
setup_test('edit page #2');
const test_page_title = 'Wikipedia:沙盒';
const test_wikitext = '\nTest edit using {{GitHub|kanasimi/wikiapi}} #2.';
const bot_name = null;
const password = null;
const zhwiki = new Wikiapi;
await zhwiki.login(bot_name, password, { API_URL: 'zh' });
// CeL.set_debug(6);
try {
await zhwiki.edit_page(test_page_title, (page_data) => {
// append text
return page_data.wikitext
+ test_wikitext;
}, {
bot: 1,
summary: 'Test edit using wikiapi module'
});
// edit successed
// reget page to test.
const page_data = await zhwiki.page(test_page_title);
assert(page_data.wikitext.endsWith(test_wikitext), 'test edit page result');
} catch (result) {
// failed to edit
handle_edit_error(assert, result);
}
// CeL.set_debug(0);
// console.log('Done.');
finish_test('edit page #2');
});
// ------------------------------------------------------------------
add_test('parse page: en', async (assert, setup_test, finish_test) => {
setup_test('parse page: en');
const user_name = null;
const password = null;
const enwiki = new Wikiapi('en');
await enwiki.login(user_name, password/*, 'en' */);
const page_data = await enwiki.page('Human');
const template_list = [];
page_data.parse().each('template',
(token) => template_list.push(token.name));
assert(template_list.includes('Speciesbox'), '[[w:en:Human]] must includes {{Speciesbox}}');
finish_test('parse page: en');
});
// ------------------------------------------------------------------
add_test('parse page: zh', async (assert, setup_test, finish_test) => {
setup_test('parse page: zh');
// Usage with other language
const zhwiki = new Wikiapi('zh');
const page_data = await zhwiki.page('宇宙');
const template_list = [];
page_data.parse().each('template',
(token) => template_list.push(token.name));
assert(template_list.includes('Infobox'), '[[w:zh:宇宙]] must includes {{Infobox}}');
finish_test('parse page: zh');
});
// ------------------------------------------------------------------
add_test('featured content: en', async (assert, setup_test, finish_test) => {
setup_test('featured content: en');
CeL.run('application.net.wiki.featured_content');
// Usage with other language
const enwiki = new Wikiapi('en');
// get only type: featured article
enwiki.get_featured_content.default_types = ['FA'];
// FC_data_hash === wiki.FC_data_hash[page_title]
const FC_data_hash = await enwiki.get_featured_content({
// get only type: featured article
// type: 'FA',
on_conflict(FC_title, data) {
CeL.warn(`Category conflict: ${data.from}→${CeL.wiki.title_link_of('Category:' + data.category, data.to)}`);
}
});
assert(FC_data_hash['Sun'].type === 'FA', '[[w:en:Sun]] is featured article');
// cache alias of {{Article history}}
const Article_history_alias = (await enwiki.redirects_here('Template:Article history'))
.map(page_data => page_data.title
// remove "Template:" prefix
.replace(/^[^:]+:/, ''));
await enwiki.for_each_page(Object.keys(FC_data_hash).filter((title) => 'FA' === FC_data_hash[title].type).slice(0, 4), async (page_data) => {
const talk_page_data = await enwiki.page(enwiki.to_talk_page(page_data));
let has_Article_history;
talk_page_data.parse().each('template',
(token) => { if (Article_history_alias.includes(token.name)) has_Article_history = true; });
assert(has_Article_history, `${CeL.wiki.title_link_of(talk_page_data)} has {{ArticleHistory}}`);
});
finish_test('featured content: en');
});
// ------------------------------------------------------------------
add_test('move page', async (assert, setup_test, finish_test) => {
setup_test('move page: testwiki');
const testwiki = new Wikiapi('test');
const move_from_title = 'move test from';
const move_to_title = 'move test to';
const reason = 'move test';
let result;
try {
result = await testwiki.move_page(move_from_title, move_to_title, { reason: reason });
assert([result.to, move_to_title], `move page: [[testwiki:${move_from_title}]]→[[testwiki:${move_to_title}]]`);
await testwiki.page(move_from_title);
result = await testwiki.move_to(move_to_title, { reason: reason, noredirect: true, movetalk: true });
// revert
await testwiki.page(move_to_title);
await testwiki.move_to(move_from_title, { reason: reason, noredirect: true, movetalk: true });
assert([result.from, move_from_title], `move page from: [[testwiki:${move_from_title}]]`);
assert([result.to, move_to_title], `move page to: [[testwiki:${move_to_title}]]`);
} catch (e) {
if (e.code !== 'missingtitle' && e.code !== 'articleexists') {
if (e.code) {
CeL.error(`[${e.code}] ${e.info}`);
} else {
console.trace(e);
}
}
assert(e === 'No csrftoken specified'
|| e.code && e.code !== 'missingtitle' && e.code !== 'articleexists',
`move page from: [[testwiki:${move_from_title}]]`);
}
finish_test('move page: testwiki');
});
// ------------------------------------------------------------------
add_test('purge page', async (assert, setup_test, finish_test) => {
setup_test('purge page: meta');
const metawiki = new Wikiapi('meta');
let page_data;
try {
page_data = await metawiki.purge('Project:Sandbox');
} catch (e) {
if (e.code === 'blocked') {
// info: 'You have been blocked from editing.'
finish_test('purge page: meta');
return;
}
}
// [ { ns: 4, title: 'Meta:Sandbox', purged: '' } ]
assert(page_data.title === 'Meta:Sandbox' && ('purged' in page_data), 'purge page: [[meta:Project:Sandbox]]');
// -----------------------------------
await metawiki.page('Meta:Babel');
page_data = await metawiki.purge({
multi: true
});
// You may also using:
// page_data = await testwiki.purge(/* no options */);
// console.log(page_data);
assert(Array.isArray(page_data) && page_data.length === 1, 'purge page: [[meta:Meta:Babel]]: multi return {Array}');
page_data = page_data[0];
assert(page_data.title === 'Meta:Babel' && ('purged' in page_data), 'purge page: [[meta:Meta:Babel]]');
finish_test('purge page: meta');
});
// ------------------------------------------------------------------
add_test('read wikidata', async (assert, setup_test, finish_test) => {
setup_test('read wikidata');
const wiki = new Wikiapi;
// Q1: Universe
const page_data = await wiki.data('Q1', {
props: 'labels|sitelinks'
});
// CeL.info('page:');
// console.log(page);
// Work with other language
assert([CeL.wiki.data.value_of(page_data.labels.zh), '宇宙'], 'zh label of Q1 is 宇宙');
finish_test('read wikidata');
});
// ------------------------------------------------------------------
add_test('read wikidata #2', async (assert, setup_test, finish_test) => {
setup_test('read wikidata #2');
const wiki = new Wikiapi('en');
// P1419: shape
const data = await wiki.data('Universe', 'P1419');
// console.log('`shape` of the `Universe`:');
// console.log(data);
assert(data.includes('shape of the universe'), '`shape` of the `Universe` is Q1647152 (shape of the universe)');
finish_test('read wikidata #2');
});
// ------------------------------------------------------------------
add_test('繁簡轉換', async (assert, setup_test, finish_test) => {
setup_test('繁簡轉換');
const wiki = new Wikiapi;
assert(['中國', await wiki.convert_Chinese('中国', { uselang: 'zh-hant' })], '繁簡轉換: 中国');
assert(['中国', await wiki.convert_Chinese('中國', { uselang: 'zh-hans' })], '繁簡轉換: 中國');
assert(['繁体,简体', (await wiki.convert_Chinese(['繁體', '簡體'], { uselang: 'zh-hans' })).join()], '繁簡轉換: {Array}');
finish_test('繁簡轉換');
});
// ------------------------------------------------------------------
add_test('tracking revisions to lookup what revision had added "an international team led by scientists"', async (assert, setup_test, finish_test) => {
setup_test('tracking revisions to lookup what revision had added "an international team led by scientists"');
const wiki = new Wikiapi('en.wikinews');
// trace https://en.wikinews.org/w/index.php?title=Study_suggests_Mars_hosted_life-sustaining_habitat_for_millions_of_years&diff=4434584&oldid=4434582
const newer_revision = await wiki.tracking_revisions('Study suggests Mars hosted life-sustaining habitat for millions of years', 'an international team led by scientists');
assert([4434584, newer_revision.revid], 'tracking revisions: Get the revid added the text');
assert(newer_revision.diff_list[0][0].includes('a team led by scientists'), 'tracking revisions: Get the text removed');
assert(newer_revision.diff_list[0][1].includes('an international team led by scientists'), 'tracking revisions: Get the text added');
finish_test('tracking revisions to lookup what revision had added "an international team led by scientists"');
});
add_test('tracking revisions to lookup what revision had added "金星快车效果图"', async (assert, setup_test, finish_test) => {
setup_test('tracking revisions to lookup what revision had added "金星快车效果图"');
const wiki = new Wikiapi('zh.wikinews');
// trace https://zh.wikinews.org/w/index.php?title=%E9%87%91%E6%98%9F%E5%BF%AB%E8%BD%A6%E5%8F%91%E5%9B%9E%E4%BA%91%E5%B1%82%E7%85%A7%E7%89%87&diff=12260&oldid=12259
const newer_revision = await wiki.tracking_revisions('金星快车发回云层照片', '金星快车效果图');
assert([12260, newer_revision.revid], 'tracking revisions: Get the revid added the text');
assert(['[[Image:Venus_express.jpg|thumb|200px|金星快车效果图]]', newer_revision.diff_list[0][1]], 'tracking revisions: Get the text added');
finish_test('tracking revisions to lookup what revision had added "金星快车效果图"');
});
// ------------------------------------------------------------------
add_test('get list of categorymembers', async (assert, setup_test, finish_test) => {
setup_test('get list of [[w:en:Category:Chemical_elements]]');
const wiki = new Wikiapi;
const list = await wiki.categorymembers('Chemical elements');
assert(list.map((page_data) => page_data.title).includes('Iron'), 'Iron is a chemical element');
finish_test('get list of [[w:en:Category:Chemical_elements]]');
});
// ------------------------------------------------------------------
add_test('get pages transclude specified template', async (assert, setup_test, finish_test) => {
setup_test('get pages transclude {{w:en:Periodic table}}');
const wiki = new Wikiapi;
const list = await wiki.embeddedin('Template:Periodic table');
assert(list.map((page_data) => page_data.title).includes('Periodic table'), '[[w:en:Periodic table]] transclude {{w:en:Periodic table}}');
finish_test('get pages transclude {{w:en:Periodic table}}');
});
// ------------------------------------------------------------------
add_test('get list of categorymembers using for_each', async (assert, setup_test, finish_test) => {
setup_test('get list of [[w:en:Category:Wikimedia Cloud Services]] using for_each');
const wiki = new Wikiapi('en');
let has_category_count = 0;
const page_list_proto = await wiki.for_each('categorymembers', 'Wikimedia Cloud Services', async (category) => {
const page_data = await wiki.page(category);
const parsed = page_data.parse();
const to_exit = parsed.each.exit;
// console.log(page_data.revisions[0].slots.main['*']);
// console.log(parsed);
parsed.each('category', (token) => {
if (token.name === 'Wikimedia Cloud Services') {
has_category_count++;
return to_exit;
}
});
});
// console.log(page_list_proto);
// console.log([page_list_proto.length, has_category_count]);
assert([page_list_proto.length, has_category_count], 'Count of [[w:en:Category:Wikimedia Cloud Services]] using for_each');
finish_test('get list of [[w:en:Category:Wikimedia Cloud Services]] using for_each');
});
add_test('get list of categorymembers using for_each_page', async (assert, setup_test, finish_test) => {
setup_test('get list of [[w:en:Category:Wikimedia Cloud Services]] using for_each_page');
const wiki = new Wikiapi('en');
let has_category_count = 0;
const page_list = await wiki.categorymembers('Wikimedia Cloud Services');
await wiki.for_each_page(page_list, (page_data) => {
const parsed = page_data.parse();
// console.log(parsed);
assert([page_data.wikitext, parsed.toString()], 'wikitext parser check');
let has_category;
parsed.each('category', (token) => {
if (token.name === 'Wikimedia Cloud Services') {
has_category = true;
}
});
if (has_category) {
has_category_count++;
}
});
// console.log([page_list.length, has_category_count]);
assert([page_list.length, has_category_count], 'Count of [[w:en:Category:Wikimedia Cloud Services]] using for_each_page');
finish_test('get list of [[w:en:Category:Wikimedia Cloud Services]] using for_each_page');
});
// ------------------------------------------------------------------
add_test('list category tree', async (assert, setup_test, finish_test) => {
setup_test('list category tree: Countries in North America');
const enwiki = new Wikiapi('en');
const page_list = await enwiki.category_tree('Countries in North America', 1);
assert(page_list.some(page_data => page_data.title === 'United States'), 'list category tree: [[Category:Countries in North America]] must includes [[United States]]');
assert('Mexico' in page_list[Wikiapi.KEY_subcategories], 'list category tree: [[Category:Mexico]] is a subcategory of [[Category:Countries in North America]]');
finish_test('list category tree: Countries in North America');
});
// ------------------------------------------------------------------
add_test('search pages include key', async (assert, setup_test, finish_test) => {
setup_test('search pages include key: 霍金');
const zhwikinews = new Wikiapi('zh.wikinews');
const page_list = await zhwikinews.search('"霍金"');
// node.js v12 does not support the optional chaining operator (?.)
// EoL of node.js v12: 2022-04-30
//assert(page_list?.some(page_data => page_data?.title === '霍金访问香港'), 'search pages include key: "霍金" must includes [[n:zh:霍金访问香港]]');
assert(page_list.some(page_data => page_data.title === '霍金访问香港'), 'search pages include key: "霍金" must includes [[n:zh:霍金访问香港]]');
finish_test('search pages include key: 霍金');
});
// ------------------------------------------------------------------
add_test('query MediaWiki API manually', async (assert, setup_test, finish_test) => {
setup_test('query MediaWiki API manually');
const wiki = new Wikiapi('mediawiki');
const results = await wiki.query({
action: "flow-parsoid-utils",
content: "bold & italic",
title: "MediaWiki", from: "html", to: "wikitext"
});
// node.js v12 does not support the optional chaining operator (?.)
// EoL of node.js v12: 2022-04-30
//assert(["'''bold''' & ''italic''", results['flow-parsoid-utils']?.content], 'query MediaWiki API manually: flow-parsoid-utils');
assert(["'''bold''' & ''italic''", results['flow-parsoid-utils'].content], 'query MediaWiki API manually: flow-parsoid-utils');
finish_test('query MediaWiki API manually');
});