From f637c4759d86572236edb17cc97492f23ba2c90b Mon Sep 17 00:00:00 2001 From: MillenniumEarl Date: Fri, 2 Oct 2020 14:01:51 +0200 Subject: [PATCH] Code coverage of 90%, added browser isolation and possibility to set cache dir --- app/index.js | 86 ++++++++++++----- app/scripts/classes/game-download.js | 4 +- app/scripts/classes/game-info.js | 14 ++- app/scripts/classes/login-result.js | 4 +- app/scripts/classes/user-data.js | 4 +- app/scripts/game-scraper.js | 10 +- app/scripts/puppeteer-helper.js | 2 + app/scripts/shared.js | 138 +++++++++++++++++++++------ app/scripts/urls-helper.js | 3 +- f95api-1.0.0.tgz | Bin 0 -> 44201 bytes package.json | 9 +- test/index-test.js | 52 +++++++++- 12 files changed, 259 insertions(+), 67 deletions(-) create mode 100644 f95api-1.0.0.tgz diff --git a/app/index.js b/app/index.js index e4da906..30df431 100644 --- a/app/index.js +++ b/app/index.js @@ -2,7 +2,6 @@ // Core modules const fs = require('fs'); -const path = require('path'); // Public modules from npm const urlExist = require('url-exist'); @@ -19,19 +18,15 @@ const { prepareBrowser, preparePage } = require('./scripts/puppeteer-helper.js'); -const GameInfo = require('./scripts/classes/game-info.js').GameInfo; -const LoginResult = require('./scripts/classes/login-result.js').LoginResult; -const UserData = require('./scripts/classes/user-data.js').UserData; +const GameInfo = require('./scripts/classes/game-info.js'); +const LoginResult = require('./scripts/classes/login-result.js'); +const UserData = require('./scripts/classes/user-data.js'); -//#region Directories -const CACHE_PATH = './f95cache'; -const COOKIES_SAVE_PATH = path.join(CACHE_PATH, 'cookies.json'); -const ENGINES_SAVE_PATH = path.join(CACHE_PATH, 'engines.json'); -const STATUSES_SAVE_PATH = path.join(CACHE_PATH, 'statuses.json'); - -// Create directory if it doesn't exist -if (!fs.existsSync(CACHE_PATH)) fs.mkdirSync(CACHE_PATH); -//#endregion Directories +//#region Expose classes +module.exports.GameInfo = GameInfo; +module.exports.LoginResult = LoginResult; +module.exports.UserData = UserData; +//#endregion Expose classes //#region Exposed properties /** @@ -44,8 +39,24 @@ module.exports.debug = function (value) { module.exports.isLogged = function () { return shared.isLogged; }; +module.exports.isolation = function(value) { + shared.isolation = value; +} +module.exports.getCacheDir = function() { + return shared.cacheDir; +} +module.exports.setCacheDir = function(value) { + shared.cacheDir = value; + + // Create directory if it doesn't exist + if (!fs.existsSync(shared.cacheDir)) fs.mkdirSync(shared.cacheDir); +} //#endregion Exposed properties +//#region Global variables +var _browser = null; +//#endregion + //#region Export methods /** * @public @@ -77,7 +88,14 @@ module.exports.login = async function (username, password) { // Else, log in throught browser if (shared.debug) console.log('No saved sessions or expired session, login on the platform'); - let browser = await prepareBrowser(); + + let browser = null; + if (shared.isolation) browser = await prepareBrowser(); + else { + if (_browser === null) _browser = await prepareBrowser(); + browser = _browser; + } + let result = await loginF95(browser, username, password); shared.isLogged = result.success; @@ -88,7 +106,7 @@ module.exports.login = async function (username, password) { } else { console.warn('Error during authentication: ' + result.message); } - await browser.close(); + if (shared.isolation) await browser.close(); return result; } /** @@ -107,7 +125,13 @@ module.exports.loadF95BaseData = async function () { if (shared.debug) console.log('Loading base data...'); // Prepare a new web page - let browser = await prepareBrowser(); + let browser = null; + if (shared.isolation) browser = await prepareBrowser(); + else { + if (_browser === null) _browser = await prepareBrowser(); + browser = _browser; + } + let page = await preparePage(browser); // Set new isolated page await page.setCookie(...shared.cookies); // Set cookies to avoid login @@ -119,18 +143,18 @@ module.exports.loadF95BaseData = async function () { // Obtain engines (disc/online) await page.waitForSelector(constSelectors.ENGINE_ID_SELECTOR); shared.engines = await loadValuesFromLatestPage(page, - ENGINES_SAVE_PATH, + shared.enginesCachePath, constSelectors.ENGINE_ID_SELECTOR, 'engines'); // Obtain statuses (disc/online) await page.waitForSelector(constSelectors.STATUS_ID_SELECTOR); shared.statuses = await loadValuesFromLatestPage(page, - STATUSES_SAVE_PATH, + shared.statusesCachePath, constSelectors.STATUS_ID_SELECTOR, 'statuses'); - await browser.close(); + if (shared.isolation) await browser.close(); if (shared.debug) console.log('Base data loaded'); return true; } @@ -169,7 +193,12 @@ module.exports.getGameData = async function (name, includeMods) { } // Gets the search results of the game being searched for - let browser = await prepareBrowser(); + let browser = null; + if (shared.isolation) browser = await prepareBrowser(); + else { + if (_browser === null) _browser = await prepareBrowser(); + browser = _browser; + } let urlList = await getSearchGameResults(browser, name); // Process previous partial results @@ -187,7 +216,7 @@ module.exports.getGameData = async function (name, includeMods) { else result.push(info); } - await browser.close(); + if (shared.isolation) await browser.close(); return result; } /** @@ -202,7 +231,12 @@ module.exports.getUserData = async function () { } // Prepare a new web page - let browser = await prepareBrowser(); + let browser = null; + if (shared.isolation) browser = await prepareBrowser(); + else { + if (_browser === null) _browser = await prepareBrowser(); + browser = _browser; + } let page = await preparePage(browser); // Set new isolated page await page.setCookie(...shared.cookies); // Set cookies to avoid login await page.goto(constURLs.F95_BASE_URL); // Go to base page @@ -227,7 +261,7 @@ module.exports.getUserData = async function () { ud.watchedThreads = await threads; await page.close(); - await browser.close(); + if (shared.isolation) await browser.close(); return ud; } @@ -247,9 +281,9 @@ module.exports.logout = function() { */ function loadCookies() { // Check the existence of the cookie file - if (fs.existsSync(COOKIES_SAVE_PATH)) { + if (fs.existsSync(shared.cookiesCachePath)) { // Read cookies - let cookiesJSON = fs.readFileSync(COOKIES_SAVE_PATH); + let cookiesJSON = fs.readFileSync(shared.cookiesCachePath); let cookies = JSON.parse(cookiesJSON); // Check if the cookies have expired @@ -376,7 +410,7 @@ async function loginF95(browser, username, password) { // Save cookies to avoid re-auth if (result.success) { let c = await page.cookies(); - fs.writeFileSync(COOKIES_SAVE_PATH, JSON.stringify(c)); + fs.writeFileSync(shared.cookiesCachePath, JSON.stringify(c)); result.message = 'Authentication successful'; } // Obtain the error message diff --git a/app/scripts/classes/game-download.js b/app/scripts/classes/game-download.js index 32a3448..8bc25ad 100644 --- a/app/scripts/classes/game-download.js +++ b/app/scripts/classes/game-download.js @@ -1,3 +1,5 @@ +'use strict'; + class GameDownload { constructor() { /** @@ -30,7 +32,7 @@ class GameDownload { } } -module.exports.GameDownload = GameDownload; +module.exports = GameDownload; function downloadMEGA(url){ diff --git a/app/scripts/classes/game-info.js b/app/scripts/classes/game-info.js index 8e79439..00482fe 100644 --- a/app/scripts/classes/game-info.js +++ b/app/scripts/classes/game-info.js @@ -1,7 +1,10 @@ +'use strict'; + const UNKNOWN = 'Unknown'; class GameInfo { constructor() { + //#region Properties /** * Game name * @type String @@ -68,9 +71,13 @@ class GameInfo { */ this.gameDir = UNKNOWN; /** - * + * Information on game file download links, + * including information on hosting platforms + * and operating system supported by the specific link + * @type GameDownload[] */ this.downloadInfo = []; + //#endregion Properties } /** @@ -89,7 +96,8 @@ class GameInfo { lastUpdate: this.lastUpdate, lastPlayed: this.lastPlayed, isMod: this.isMod, - gameDir: this.gameDir + gameDir: this.gameDir, + downloadInfo: this.downloadInfo } } @@ -102,4 +110,4 @@ class GameInfo { return Object.assign(new GameInfo(), json); } } -module.exports.GameInfo = GameInfo; \ No newline at end of file +module.exports = GameInfo; \ No newline at end of file diff --git a/app/scripts/classes/login-result.js b/app/scripts/classes/login-result.js index 669ef68..9ed02dd 100644 --- a/app/scripts/classes/login-result.js +++ b/app/scripts/classes/login-result.js @@ -1,3 +1,5 @@ +'use strict'; + /** * Object obtained in response to an attempt to login to the portal. */ @@ -15,4 +17,4 @@ class LoginResult { this.message = ''; } } -module.exports.LoginResult = LoginResult; \ No newline at end of file +module.exports = LoginResult; \ No newline at end of file diff --git a/app/scripts/classes/user-data.js b/app/scripts/classes/user-data.js index 31de140..6acb128 100644 --- a/app/scripts/classes/user-data.js +++ b/app/scripts/classes/user-data.js @@ -1,3 +1,5 @@ +'use strict'; + /** * Class containing the data of the user currently connected to the F95Zone platform. */ @@ -21,4 +23,4 @@ class UserData { } } -module.exports.UserData = UserData; \ No newline at end of file +module.exports = UserData; \ No newline at end of file diff --git a/app/scripts/game-scraper.js b/app/scripts/game-scraper.js index 87ad00a..b44eeff 100644 --- a/app/scripts/game-scraper.js +++ b/app/scripts/game-scraper.js @@ -1,3 +1,5 @@ +'use strict'; + // Public modules from npm const HTMLParser = require('node-html-parser'); const puppeteer = require('puppeteer'); @@ -7,8 +9,8 @@ const urlExist = require('url-exist'); const shared = require('./shared.js'); const selectors = require('./costants/css-selectors.js'); const { preparePage } = require('./puppeteer-helper.js'); -const GameDownload = require('./classes/game-download.js').GameDownload; -const GameInfo = require('./classes/game-info.js').GameInfo; +const GameDownload = require('./classes/game-download.js'); +const GameInfo = require('./classes/game-info.js'); const { isStringAValidURL, isF95URL } = require('./urls-helper.js'); /** @@ -154,8 +156,8 @@ async function getGamePreviewSource(page) { // Get the firs image available let img = document.querySelector(selector); - if (img === null || img === undefined) return null; - else return img.getAttribute('src'); + if (img) return img.getAttribute('src'); + else return null; }, selectors.GAME_IMAGES); // Check if the URL is valid diff --git a/app/scripts/puppeteer-helper.js b/app/scripts/puppeteer-helper.js index edcfd46..d346892 100644 --- a/app/scripts/puppeteer-helper.js +++ b/app/scripts/puppeteer-helper.js @@ -1,3 +1,5 @@ +'use strict'; + // Public modules from npm const puppeteer = require('puppeteer'); diff --git a/app/scripts/shared.js b/app/scripts/shared.js index d59e34c..d6643ec 100644 --- a/app/scripts/shared.js +++ b/app/scripts/shared.js @@ -1,71 +1,155 @@ +'use strict'; + +// Core modules +const { join } = require('path'); + /** * Class containing variables shared between modules. */ class Shared { + //#region Properties /** * Shows log messages and other useful functions for module debugging. + * @type Boolean */ static _debug = false; + /** + * Indicates whether a user is logged in to the F95Zone platform or not. + * @type Boolean + */ static _isLogged = false; + /** + * List of cookies obtained from the F95Zone platform. + * @type Object[] + */ static _cookies = null; + /** + * List of possible game engines used for development. + * @type String[] + */ static _engines = null; + /** + * List of possible development statuses that a game can assume. + * @type String[] + */ static _statuses = null; + /** + * Wait instruction for the browser created by puppeteer. + * @type String + */ static WAIT_STATEMENT = 'domcontentloaded'; + /** + * Path to the directory to save the cache generated by the API. + * @type String + */ + static _cacheDir = './f95cache'; + /** + * If true, it opens a new browser for each request to + * the F95Zone platform, otherwise it reuses the same. + * @type Boolean + */ + static _isolation = false; + //#endregion Properties - static set debug(val) { - this._debug = val; - } - + //#region Getters /** * Shows log messages and other useful functions for module debugging. - * @returns {boolean} + * @returns {Boolean} */ static get debug() { return this._debug; } - - static set isLogged(val) { - this._isLogged = val; - } - /** * @returns {boolean} */ static get isLogged() { return this._isLogged; } - - static set cookies(val) { - this._cookies = val; - } - /** * @returns {object[]} */ static get cookies() { return this._cookies; } + /** + * @returns {String[]} + */ + static get engines() { + return this._engines; + } + /** + * @returns {String[]} + */ + static get statuses() { + return this._statuses; + } + /** + * Directory to save the API cache. + * @returns {String} + */ + static get cacheDir() { + return this._cacheDir; + } + /** + * Path to the F95 platform cache. + * @returns {String} + */ + static get cookiesCachePath() { + return join(this._cacheDir, 'cookies.json'); + } + /** + * Path to the game engine cache. + * @returns {String} + */ + static get enginesCachePath() { + return join(this._cacheDir, 'engines.json'); + } + /** + * Path to the cache of possible game states. + * @returns {String} + */ + static get statusesCachePath() { + return join(this._cacheDir, 'statuses.json'); + } + /** + * If true, it opens a new browser for each request + * to the F95Zone platform, otherwise it reuses the same. + * @returns {Boolean} + */ + static get isolation() { + return this._isolation; + } + //#endregion Getters + + //#region Setters + static set cookies(val) { + this._cookies = val; + } static set engines(val) { this._engines = val; } - /** - * @returns {string[]} - */ - static get engines() { - return this._engines; - } - static set statuses(val) { this._statuses = val; } - /** - * @returns {string[]} - */ - static get statuses() { - return this._statuses; + + static set cacheDir(val) { + this._cacheDir = val; } + + static set debug(val) { + this._debug = val; + } + + static set isLogged(val) { + this._isLogged = val; + } + + static set isolation(val) { + this._isolation = val; + } + //#endregion Setters } module.exports = Shared; \ No newline at end of file diff --git a/app/scripts/urls-helper.js b/app/scripts/urls-helper.js index 569b60f..a1865ed 100644 --- a/app/scripts/urls-helper.js +++ b/app/scripts/urls-helper.js @@ -1,7 +1,8 @@ +'use strict'; + // Modules from file const { F95_BASE_URL } = require('./costants/urls.js'); - /** * @protected * Check if the url belongs to the domain of the F95 platform. diff --git a/f95api-1.0.0.tgz b/f95api-1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7105301ee921f99cf503c7968655fcc18fae9afa GIT binary patch literal 44201 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

_Fa%U}(<=nn_tH`+$qVJyz>RX?nr6C%b`Q|I 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 VlRx>BKl%TezX5<&hSmUz0RSp82PFUi literal 0 HcmV?d00001 diff --git a/package.json b/package.json index b6455ef..6dc6b28 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,16 @@ "version": "1.0.0", "description": "Unofficial Node JS module for scraping F95Zone platform", "main": "./app/index.js", + "repository": { + "type": "git", + "url": "https://github.com/MillenniumEarl/F95API.git" + }, + "license": "UNLICENSED", + "private": true, "scripts": { "unit-test": "nyc --reporter=text mocha", - "test": "node ./app/test.js" + "test": "node ./app/test.js", + "deploy": "npm pack" }, "keywords": [ "f95zone", diff --git a/test/index-test.js b/test/index-test.js index 0e45f99..1b7299b 100644 --- a/test/index-test.js +++ b/test/index-test.js @@ -1,7 +1,6 @@ const expect = require("chai").expect; const F95API = require("../app/index"); const fs = require("fs"); -const { debug } = require("console"); const COOKIES_SAVE_PATH = "./f95cache/cookies.json"; const ENGINES_SAVE_PATH = "./f95cache/engines.json"; @@ -11,6 +10,8 @@ const PASSWORD = "f9vTcRNuvxj4YpK"; const FAKE_USERNAME = "FakeUsername091276"; const FAKE_PASSWORD = "fake_password"; +F95API.isolation(true); + describe("Login without cookies", function () { //#region Set-up this.timeout(30000); // All tests in this suite get 30 seconds before timeout @@ -48,7 +49,7 @@ describe("Login with cookies", function () { //#region Set-up this.timeout(30000); // All tests in this suite get 30 seconds before timeout - before("Log in to create cookies", async function () { + before("Log in to create cookies then logout", async function () { // Runs once before the first test in this block if (!fs.existsSync(COOKIES_SAVE_PATH)) await F95API.login(USERNAME, PASSWORD); // Download cookies F95API.logout(); @@ -127,4 +128,51 @@ describe("Search game data", function () { const result = await F95API.getGameData("Kingdom of Deception", false); expect(result, "Without being logged should return null").to.be.null; }); +}); + +describe("Load user data", function () { + //#region Set-up + this.timeout(30000); // All tests in this suite get 30 seconds before timeout + //#endregion Set-up + + it("Retrieve when logged", async function () { + // Login + await F95API.login(USERNAME, PASSWORD); + + // Then retrieve user data + let data = await F95API.getUserData(); + + expect(data).to.exist; + expect(data.username).to.equal(USERNAME); + }); + it("Retrieve when not logged", async function () { + // Logout + F95API.logout(); + + // Try to retrieve user data + let data = await F95API.getUserData(); + + expect(data).to.be.null; + }); +}); + +describe("Check game version", function () { + //#region Set-up + this.timeout(30000); // All tests in this suite get 30 seconds before timeout + //#endregion Set-up + + it("Get game version", async function () { + const loginResult = await F95API.login(USERNAME, PASSWORD); + expect(loginResult.success).to.be.true; + + const loadResult = await F95API.loadF95BaseData(); + expect(loadResult).to.be.true; + + // This test depend on the data on F95Zone at + // https://f95zone.to/threads/kingdom-of-deception-v0-10-8-hreinn-games.2733/ + const result = (await F95API.getGameData("Kingdom of Deception", false))[0]; + + let version = await F95API.getGameVersion(result); + expect(version).to.be.equal(result.version); + }); }); \ No newline at end of file