From 167d4fe0cd782479db6972982f5853f6061f57db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20R=C3=A8gne?= Date: Sat, 25 Jan 2020 19:23:44 +0100 Subject: [PATCH] refactor: Simplifier le code source. --- .metalint/eslint.json | 2 +- .metalint/eslint_nodejs.json | 1 - package.json | 1 + src/background/menu.js | 13 ++++-- src/core/i18n.js | 5 +-- src/core/index.js | 66 +++++++++++++++++++-------- src/core/jsonrpc.js | 31 ------------- src/core/notify.js | 2 - src/popup/script.js | 80 +++++++++++++++++++-------------- src/tools/matchpattern.js | 2 +- test/polyfill.js | 2 - test/polyfill/browser.js | 26 ++++++++--- test/polyfill/btoa.js | 5 --- test/unit/core/notify.js | 54 +++++++++++++--------- test/unit/core/pebkac.js | 51 ++++++++++++++++----- test/unit/core/scraper/radio.js | 1 + test/unit/core/scrapers.js | 2 +- test/unit/locales.js | 8 +++- test/unit/mock/fetch.js | 2 +- 19 files changed, 210 insertions(+), 144 deletions(-) delete mode 100644 test/polyfill/btoa.js diff --git a/.metalint/eslint.json b/.metalint/eslint.json index 9bc4fee3..aef8b93f 100644 --- a/.metalint/eslint.json +++ b/.metalint/eslint.json @@ -80,7 +80,7 @@ "no-constructor-return": 2, "no-div-regex": 2, "no-else-return": [2, { "allowElseIf": false }], - "no-empty-function": 2, + "no-empty-function": [2, { "allow": ["arrowFunctions"] }], "no-empty-pattern": 2, "no-eq-null": 0, "no-eval": 2, diff --git a/.metalint/eslint_nodejs.json b/.metalint/eslint_nodejs.json index b4b38ee1..81ee6366 100644 --- a/.metalint/eslint_nodejs.json +++ b/.metalint/eslint_nodejs.json @@ -10,7 +10,6 @@ "globals": { "URL": "readonly", "browser": "readonly", - "btoa": "readonly", "DOMParser": "readonly", "fetch": "readonly", "WebSocket": "readonly" diff --git a/package.json b/package.json index da5cd339..ec958761 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "node-fetch": "^2.6.0", "nyc": "^15.0.0", "purgecss": "^2.0.5", + "sinon": "^8.1.0", "standard-version": "^7.1.0", "stylelint": "^13.0.0", "stylelint-order": "^4.0.0", diff --git a/src/background/menu.js b/src/background/menu.js index 44093c2d..d9e3d3c5 100644 --- a/src/background/menu.js +++ b/src/background/menu.js @@ -3,6 +3,7 @@ */ import { cast } from "../core/index.js"; +import { notify } from "../core/notify.js"; /** * Agrège les liens des différents points d'entrée. @@ -13,9 +14,9 @@ import { cast } from "../core/index.js"; */ const aggregate = async function (info) { if ("bookmarkId" in info) { - const bookmark = await browser.bookmarks.get(info.bookmarkId); - return ["url" in bookmark[0] ? bookmark[0].url - : ""]; + const bookmarks = await browser.bookmarks.get(info.bookmarkId); + return bookmarks.map((b) => ("url" in b ? b.url + : b.title)); } return [ @@ -33,7 +34,11 @@ const click = async function (info) { if ("send" === info.menuItemId || "insert" === info.menuItemId || "add" === info.menuItemId) { const urls = await aggregate(info); - cast(info.menuItemId, urls); + try { + await cast(info.menuItemId, urls); + } catch (err) { + notify(err); + } } else if (!info.wasChecked) { browser.storage.local.set({ "server-active": parseInt(info.menuItemId, 10) diff --git a/src/core/i18n.js b/src/core/i18n.js index 8337af44..7a2bb2d1 100644 --- a/src/core/i18n.js +++ b/src/core/i18n.js @@ -47,10 +47,9 @@ for (const element of walk()) { element.textContent = value; } else { for (const node of element.childNodes) { - if ("#text" !== node.nodeName) { - continue; + if ("#text" === node.nodeName) { + node.nodeValue = node.nodeValue.replace("{}", value); } - node.nodeValue = node.nodeValue.replace("{}", value); } } } else { diff --git a/src/core/index.js b/src/core/index.js index bcb43697..ddd35296 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -2,11 +2,17 @@ * @module */ -import { jsonrpc } from "./jsonrpc.js"; -import { notify } from "./notify.js"; +import { JSONRPC } from "./jsonrpc.js"; import { PebkacError } from "./pebkac.js"; import { extract } from "./scrapers.js"; +/** + * Le client JSON-RPC pour contacter Kodi. + * + * @type {object} + */ +export const jsonrpc = new JSONRPC(""); + /** * Récupère le lien à analyser parmi les données récupérées. * @@ -30,12 +36,30 @@ export const mux = function (urls) { (/^magnet:.*$/iu).test(url) || (/^acestream:.*$/iu).test(url)); } catch { - // Ignorer l'erreur provenant d'une URL invalide. + // Indiquer que la construction de l'URL a échouée. return false; } }); }; +/** + * Crée le client JSON-RPC pour contacter Kodi. + * + * @param {object} changes Les paramètres modifiés dans la configuration. + */ +const change = async function (changes) { + // Ignorer tous les changements sauf ceux liés au serveur. + if (!Object.entries(changes).some(([k, v]) => k.startsWith("server-") && + "newValue" in v)) { + return; + } + + const config = await browser.storage.local.get(); + jsonrpc.close(); + jsonrpc.host = config["server-list"][config["server-active"]].host; + jsonrpc.onChanged(); +}; + /** * Diffuse un média sur Kodi. * @@ -43,28 +67,32 @@ export const mux = function (urls) { * @param {string} action L'action à effectuer ("send", * "insert" ou "add"). * @param {Array.} urls La liste des éventuelles URLs. - * @returns {Promise.} Une promesse tenue ou rejetée. + * @returns {Promise.} Une promesse tenue vide. */ export const cast = async function (action, urls) { const url = mux(urls); if (undefined === url) { - return notify(1 === urls.length ? new PebkacError("noLink", urls[0]) - : new PebkacError("noLinks")); + throw 1 === urls.length ? new PebkacError("noLink", urls[0]) + : new PebkacError("noLinks"); } - try { - const file = await extract(new URL(url), { "depth": 0 }); - switch (action) { - case "send": await jsonrpc.send(file); break; - case "insert": await jsonrpc.insert(file); break; - case "add": await jsonrpc.add(file); break; - default: return notify(new Error(action + " is not supported")); - } - const config = await browser.storage.local.get(["general-history"]); - return config["general-history"] ? browser.history.addUrl({ url }) - : Promise.resolve(); - } catch (err) { - return notify(err); + const file = await extract(new URL(url), { "depth": 0 }); + switch (action) { + case "send": await jsonrpc.send(file); break; + case "insert": await jsonrpc.insert(file); break; + case "add": await jsonrpc.add(file); break; + default: throw new Error(action + " is not supported"); + } + + const config = await browser.storage.local.get(["general-history"]); + if (config["general-history"]) { + await browser.history.addUrl({ url }); } }; + +// Simuler un changement de configuration pour se connecter au bon serveur. Ce +// bidouillage est utile quand ce fichier est chargé depuis les options ou la +// popin (dans le background, cette migration qui change la configuration). +change({ "server-": { "newValue": null } }); +browser.storage.onChanged.addListener(change); diff --git a/src/core/jsonrpc.js b/src/core/jsonrpc.js index a50f8b34..7cd240f9 100644 --- a/src/core/jsonrpc.js +++ b/src/core/jsonrpc.js @@ -516,34 +516,3 @@ export const JSONRPC = class { }); } }; - -/** - * Le client JSON-RPC pour contacter Kodi. - * - * @type {object} - */ -export const jsonrpc = new JSONRPC(""); - -/** - * Crée le client JSON-RPC pour contacter Kodi. - * - * @param {object} changes Les paramètres modifiés dans la configuration. - */ -const change = async function (changes) { - // Ignorer tous les changements sauf ceux liés au serveur. - if (!Object.entries(changes).some(([k, v]) => k.startsWith("server-") && - "newValue" in v)) { - return; - } - - const config = await browser.storage.local.get(); - jsonrpc.close(); - jsonrpc.host = config["server-list"][config["server-active"]].host; - jsonrpc.onChanged(); -}; - -// Simuler un changement de configuration pour se connecter au bon serveur. Ce -// bidouillage est utile quand ce fichier est chargé depuis les options ou la -// popin (dans le background, cette migration qui change la configuration). -change({ "server-": { "newValue": null } }); -browser.storage.onChanged.addListener(change); diff --git a/src/core/notify.js b/src/core/notify.js index ac24e5cf..993b17cc 100644 --- a/src/core/notify.js +++ b/src/core/notify.js @@ -7,7 +7,6 @@ * * @function * @param {object} err L'erreur affichée dans la notification. - * @returns {Promise.} Une promesse rejetée contenant l'erreur. */ export const notify = function (err) { browser.notifications.create(null, { @@ -18,5 +17,4 @@ export const notify = function (err) { : browser.i18n.getMessage("notifications_unknown_title"), "message": err.message }); - return Promise.reject(err); }; diff --git a/src/popup/script.js b/src/popup/script.js index d1484fca..fcb7b022 100644 --- a/src/popup/script.js +++ b/src/popup/script.js @@ -2,8 +2,8 @@ * @module */ -import { cast } from "../core/index.js"; -import { jsonrpc } from "../core/jsonrpc.js"; +import { cast, jsonrpc } from "../core/index.js"; +import { notify } from "../core/notify.js"; /** * La liste des vitesses de lecture. @@ -25,7 +25,7 @@ let speed = null; /** * L'identifiant de l'intervalle faisant avancer la barre de progression. * - * @type {?number} + * @type {?Timeout} */ let interval = null; @@ -146,7 +146,7 @@ const onVolumeChanged = function (properties) { } }; -const notify = function (err) { +const splash = function (err) { const article = document.querySelector("article"); if ("PebkacError" === err.name) { article.querySelector("h1").textContent = err.title; @@ -218,7 +218,7 @@ const update = async function () { document.querySelector("#fullscreen").disabled = false; } catch (err) { - notify(err); + splash(err); } }; @@ -243,8 +243,12 @@ const send = async function () { } const url = await mux(); - await cast("send", [url]); - close(); + try { + await cast("send", [url]); + close(); + } catch (err) { + notify(err); + } }; const insert = async function () { @@ -255,8 +259,12 @@ const insert = async function () { } const url = await mux(); - await cast("insert", [url]); - close(); + try { + await cast("insert", [url]); + close(); + } catch (err) { + notify(err); + } }; const add = async function () { @@ -267,8 +275,12 @@ const add = async function () { } const url = await mux(); - await cast("add", [url]); - close(); + try { + await cast("add", [url]); + close(); + } catch (err) { + notify(err); + } }; const paste = function (event) { @@ -331,7 +343,7 @@ const previous = function () { return; } - jsonrpc.previous().catch(notify); + jsonrpc.previous().catch(splash); }; const rewind = function () { @@ -346,7 +358,7 @@ const rewind = function () { case 0: speed = 5; break; default: --speed; } - jsonrpc.setSpeed(SPEEDS[speed]).catch(notify); + jsonrpc.setSpeed(SPEEDS[speed]).catch(splash); }; const stop = function () { @@ -357,13 +369,13 @@ const stop = function () { } speed = null; - jsonrpc.stop().catch(notify); + jsonrpc.stop().catch(splash); }; const playPause = function () { if (null === speed) { speed = 5; - jsonrpc.open().catch(notify); + jsonrpc.open().catch(splash); } else if (5 === speed) { // Annuler l'action (venant d'un raccourci clavier) si le bouton est // désactivé (car la connexion à Kodi a échouée). @@ -372,10 +384,10 @@ const playPause = function () { } speed = -1; - jsonrpc.playPause().catch(notify); + jsonrpc.playPause().catch(splash); } else { speed = 5; - jsonrpc.playPause().catch(notify); + jsonrpc.playPause().catch(splash); } }; @@ -391,7 +403,7 @@ const forward = function () { case 10: speed = 5; break; default: ++speed; } - jsonrpc.setSpeed(SPEEDS[speed]).catch(notify); + jsonrpc.setSpeed(SPEEDS[speed]).catch(splash); }; const next = function () { @@ -401,7 +413,7 @@ const next = function () { return; } - jsonrpc.next().catch(notify); + jsonrpc.next().catch(splash); }; const setMute = function (event) { @@ -423,7 +435,7 @@ const setMute = function (event) { } else { document.querySelector("#volume").classList.remove("disabled"); } - jsonrpc.setMute(input.checked).catch(notify); + jsonrpc.setMute(input.checked).catch(splash); }; const setVolume = function (diff) { @@ -441,7 +453,7 @@ const setVolume = function (diff) { if (Number.isInteger(diff)) { input.valueAsNumber += diff; } - jsonrpc.setVolume(input.valueAsNumber).catch(notify); + jsonrpc.setVolume(input.valueAsNumber).catch(splash); }; const repeat = function () { @@ -468,7 +480,7 @@ const repeat = function () { one.checked = false; off.checked = true; } - jsonrpc.setRepeat().catch(notify); + jsonrpc.setRepeat().catch(splash); }; const shuffle = function () { @@ -479,7 +491,7 @@ const shuffle = function () { } const input = document.querySelector("#shuffle input"); - jsonrpc.setShuffle(input.checked).catch(notify); + jsonrpc.setShuffle(input.checked).catch(splash); }; const contextMenu = function () { @@ -489,7 +501,7 @@ const contextMenu = function () { return; } - jsonrpc.contextMenu().catch(notify); + jsonrpc.contextMenu().catch(splash); }; const up = function () { @@ -499,7 +511,7 @@ const up = function () { return; } - jsonrpc.up().catch(notify); + jsonrpc.up().catch(splash); }; const info = function () { @@ -509,7 +521,7 @@ const info = function () { return; } - jsonrpc.info().catch(notify); + jsonrpc.info().catch(splash); }; const left = function () { @@ -519,7 +531,7 @@ const left = function () { return; } - jsonrpc.left().catch(notify); + jsonrpc.left().catch(splash); }; const select = function () { @@ -529,7 +541,7 @@ const select = function () { return; } - jsonrpc.select().catch(notify); + jsonrpc.select().catch(splash); }; const right = function () { @@ -539,7 +551,7 @@ const right = function () { return; } - jsonrpc.right().catch(notify); + jsonrpc.right().catch(splash); }; const back = function () { @@ -549,7 +561,7 @@ const back = function () { return; } - jsonrpc.back().catch(notify); + jsonrpc.back().catch(splash); }; const down = function () { @@ -559,7 +571,7 @@ const down = function () { return; } - jsonrpc.down().catch(notify); + jsonrpc.down().catch(splash); }; const showOSD = function () { @@ -569,7 +581,7 @@ const showOSD = function () { return; } - jsonrpc.showOSD().catch(notify); + jsonrpc.showOSD().catch(splash); }; const setFullscreen = function () { @@ -579,7 +591,7 @@ const setFullscreen = function () { return; } - jsonrpc.setFullscreen().catch(notify); + jsonrpc.setFullscreen().catch(splash); }; const passing = function () { @@ -607,7 +619,7 @@ const move = function () { const seek = function () { interval = setInterval(passing, 1000); const time = document.querySelector("#time"); - jsonrpc.seek(time.valueAsNumber).catch(notify); + jsonrpc.seek(time.valueAsNumber).catch(splash); }; diff --git a/src/tools/matchpattern.js b/src/tools/matchpattern.js index 03b4114b..1716c411 100644 --- a/src/tools/matchpattern.js +++ b/src/tools/matchpattern.js @@ -38,7 +38,7 @@ export const compile = function (pattern) { }; /** - * Ajoute un filtre l'URL en paramètre. + * Ajoute un filtre sur l'URL en paramètre d'une fonction. * * @function * @param {Function} func La fonction qui sera filtrée. diff --git a/test/polyfill.js b/test/polyfill.js index af38d0ab..c4cb35e5 100644 --- a/test/polyfill.js +++ b/test/polyfill.js @@ -1,11 +1,9 @@ import { URL } from "url"; import { browser } from "./polyfill/browser.js"; -import { btoa } from "./polyfill/btoa.js"; import { DOMParser } from "./polyfill/domparser.js"; import { fetch } from "./polyfill/fetch.js"; globalThis.URL = URL; globalThis.fetch = fetch; globalThis.browser = browser; -globalThis.btoa = btoa; globalThis.DOMParser = DOMParser; diff --git a/test/polyfill/browser.js b/test/polyfill/browser.js index 0ec714fc..b9ca99dc 100644 --- a/test/polyfill/browser.js +++ b/test/polyfill/browser.js @@ -1,6 +1,11 @@ +import fs from "fs"; +import path from "path"; + +const I18NS = fs.readFileSync(path.join(__dirname, + "../../locales/en/messages.json")); + const data = { "bookmarks": {}, - "i18n": {}, "storage": { "local": { "data": {}, @@ -12,13 +17,24 @@ const data = { export const browser = { "bookmarks": { "get": (id) => { - return id in data.bookmarks ? Promise.resolve(data.bookmarks[id]) - : Promise.reject(new Error()); + return id in data.bookmarks + ? Promise.resolve([data.bookmarks[id]]) + : Promise.reject(new Error("Bookmark not found")); } }, "i18n": { - "getMessage": (key) => { - return data.i18n[key]; + "getMessage": (key, substitutions) => { + if (!(key in I18NS)) { + return ""; + } + if (!("placeholders" in I18NS[key])) { + return I18NS[key]; + } + return Object.keys(I18NS[key].placeholders) + .reduce((message, placeholder, index) => { + return message.replace("$" + placeholder + "$", + substitutions[index]); + }, I18NS[key].message); } }, diff --git a/test/polyfill/btoa.js b/test/polyfill/btoa.js deleted file mode 100644 index 7f5f2854..00000000 --- a/test/polyfill/btoa.js +++ /dev/null @@ -1,5 +0,0 @@ -import { Buffer } from "buffer"; - -export const btoa = function (stringToEncode) { - return Buffer.from(stringToEncode).toString("base64"); -}; diff --git a/test/unit/core/notify.js b/test/unit/core/notify.js index 27dcff41..47666a69 100644 --- a/test/unit/core/notify.js +++ b/test/unit/core/notify.js @@ -1,30 +1,40 @@ -import assert from "assert"; -import { notify } from "../../../src/core/notify.js"; +import assert from "assert"; +import sinon from "sinon"; +import { notify } from "../../../src/core/notify.js"; +import { PebkacError } from "../../../src/core/pebkac.js"; describe("core/notify.js", function () { + afterEach(function () { + sinon.restore(); + }); + describe("constructor()", function () { - it("should accept Error", async function () { - try { - await notify(new Error("Message.")); - assert.fail(); - } catch (err) { - assert.strictEqual(err.name, "Error"); - assert.strictEqual(err.message, "Message."); - } + it("should accept Error", function () { + sinon.stub(browser.notifications, "create") + .callsFake(() => {}); + sinon.stub(browser.i18n, "getMessage").callsFake((k) => k); + + notify(new Error("Message.")); + + assert.ok(browser.notifications.create.calledOnce); + const call = browser.notifications.create.firstCall; + assert.strictEqual(call.args[1].title, + "notifications_unknown_title"); + assert.strictEqual(call.args[1].message, "Message."); }); - it("should accept PebkacError", async function () { - const pebkac = { - "name": "PebkacError", - "title": "Titre", - "message": "Message." - }; - try { - await notify(pebkac); - assert.fail(); - } catch (err) { - assert.strictEqual(err, pebkac); - } + it("should accept PebkacError", function () { + sinon.stub(browser.notifications, "create") + .callsFake(() => {}); + sinon.stub(browser.i18n, "getMessage").callsFake((k) => k); + + notify(new PebkacError("clef")); + + assert.ok(browser.notifications.create.calledOnce); + const call = browser.notifications.create.firstCall; + assert.strictEqual(call.args[1].title, "notifications_clef_title"); + assert.strictEqual(call.args[1].message, + "notifications_clef_message"); }); }); }); diff --git a/test/unit/core/pebkac.js b/test/unit/core/pebkac.js index afda8fc4..61440f3f 100644 --- a/test/unit/core/pebkac.js +++ b/test/unit/core/pebkac.js @@ -1,22 +1,53 @@ import assert from "assert"; +import sinon from "sinon"; import { PebkacError } from "../../../src/core/pebkac.js"; describe("core/pebkac.js", function () { describe("constructor()", function () { + afterEach(function () { + sinon.restore(); + }); + it("should accept one parameter", function () { - const err = new PebkacError("foo"); - assert.strictEqual(err.name, "PebkacError"); - assert.strictEqual(err.type, "foo"); - assert.strictEqual(err.message, "notifications_foo_message"); - assert.strictEqual(err.title, "notifications_foo_title"); + sinon.stub(browser.i18n, "getMessage") + .callsFake((k, s) => (undefined === s + ? k + : k + ": " + s.toString())); + + const err = new PebkacError("clef"); + assert.strictEqual(err.message, "notifications_clef_message: "); + assert.strictEqual(err.name, "PebkacError"); + assert.strictEqual(err.type, "clef"); + assert.strictEqual(err.title, "notifications_clef_title"); }); it("should accept two parameters", function () { - const err = new PebkacError("bar", "baz"); - assert.strictEqual(err.name, "PebkacError"); - assert.strictEqual(err.type, "bar"); - assert.strictEqual(err.message, "notifications_bar_message: baz"); - assert.strictEqual(err.title, "notifications_bar_title"); + sinon.stub(browser.i18n, "getMessage") + .callsFake((k, s) => (undefined === s ? k + : k + ": " + s)); + + const err = new PebkacError("clef", "Substitution"); + assert.strictEqual(err.message, + "notifications_clef_message: Substitution"); + assert.strictEqual(err.name, "PebkacError"); + assert.strictEqual(err.type, "clef"); + assert.strictEqual(err.title, "notifications_clef_title"); + }); + + it("should accept two parameters (even an array)", function () { + sinon.stub(browser.i18n, "getMessage") + .callsFake((k, s) => (undefined === s + ? k + : k + ": " + s.toString())); + + const err = new PebkacError("clef", ["1ère substitution", + "2ème substitution"]); + assert.strictEqual(err.message, + "notifications_clef_message: 1ère substitution," + + "2ème substitution"); + assert.strictEqual(err.name, "PebkacError"); + assert.strictEqual(err.type, "clef"); + assert.strictEqual(err.title, "notifications_clef_title"); }); }); }); diff --git a/test/unit/core/scraper/radio.js b/test/unit/core/scraper/radio.js index 9bc5b482..489626a7 100644 --- a/test/unit/core/scraper/radio.js +++ b/test/unit/core/scraper/radio.js @@ -54,6 +54,7 @@ describe("core/scraper/radio.js", function () { const file = await extract(new URL(url), doc); assert.strictEqual(file, expected); }); + it("should return audio URL", async function () { const url = "https://www.radio.net/s/baz"; const doc = new DOMParser().parseFromString(` diff --git a/test/unit/core/scrapers.js b/test/unit/core/scrapers.js index 0b968068..7323055e 100644 --- a/test/unit/core/scrapers.js +++ b/test/unit/core/scrapers.js @@ -25,7 +25,7 @@ describe("core/scrapers.js", function () { const url = "http://www.dailymotion.com/video/x17qw0a"; const expected = "plugin://plugin.video.dailymotion_com/"; - const file = await extract(new URL(url), null, { "depth": 0 }); + const file = await extract(new URL(url), { "depth": 0 }); assert.ok(file.startsWith(expected), `"${file}".startsWith(expected)`); }); diff --git a/test/unit/locales.js b/test/unit/locales.js index dcb88946..4d0d670b 100644 --- a/test/unit/locales.js +++ b/test/unit/locales.js @@ -1,6 +1,5 @@ import assert from "assert"; -import fr from "../../src/_locales/fr/messages.json"; -import en from "../../src/_locales/en/messages.json"; +import fs from "fs"; const compare = (messages1, messages2) => { for (const [name, message] of Object.entries(messages1)) { @@ -32,6 +31,11 @@ const compare = (messages1, messages2) => { describe("_locales", function () { it("should have same messages", function () { + const fr = JSON.parse(fs.readFileSync("src/_locales/fr/messages.json", + "utf8")); + const en = JSON.parse(fs.readFileSync("src/_locales/en/messages.json", + "utf8")); + compare(fr, en); compare(en, fr); }); diff --git a/test/unit/mock/fetch.js b/test/unit/mock/fetch.js index 6d839121..057ab5cd 100644 --- a/test/unit/mock/fetch.js +++ b/test/unit/mock/fetch.js @@ -14,7 +14,7 @@ export const fetch = (input, init = {}) => { if (err) { resolve(nodeFetch(input, init)); } else { - resolve({ "json": () => JSON.parse(data) }); + resolve({ "json": () => JSON.parse(data.toString()) }); } }); });