From 3fdc518de617465c3e2b713150231a80414b96c5 Mon Sep 17 00:00:00 2001 From: Jae Sung Park Date: Wed, 30 Aug 2023 18:18:58 +0900 Subject: [PATCH] test(module): Add test for module & category (#3390) - update some browser & worker module as function to facilitate test - add test case for internal/category Ref #3333 --- karma.conf.cjs | 16 +++++++ src/ChartInternal/internals/category.ts | 4 +- src/module/browser.ts | 49 +++++++++++++++------ src/module/worker.ts | 38 ++++++++++------ test/api/category-spec.ts | 26 ++++++++++- test/module/module-spec.ts | 58 ++++++++++++++++++++++++- 6 files changed, 158 insertions(+), 33 deletions(-) diff --git a/karma.conf.cjs b/karma.conf.cjs index cd4bd3e52..674b9570d 100644 --- a/karma.conf.cjs +++ b/karma.conf.cjs @@ -47,6 +47,22 @@ module.exports = function(config) { target: ["web", "es5"], module: { rules: [ + { + test: require.resolve("./src/module/browser.ts"), + loader: "exports-loader", + options: { + type: "module", + exports: ["getGlobal", "getFallback"] + } + }, + { + test: require.resolve("./src/module/worker.ts"), + loader: "exports-loader", + options: { + type: "module", + exports: "getWorker" + } + }, { test: /(\.[jt]s)$/, loader: "babel-loader", diff --git a/src/ChartInternal/internals/category.ts b/src/ChartInternal/internals/category.ts index a44ca9d5a..b7764aab7 100644 --- a/src/ChartInternal/internals/category.ts +++ b/src/ChartInternal/internals/category.ts @@ -10,8 +10,8 @@ export default { * @private */ categoryName(i: number): string { - const {axis_x_categories: categories = []} = this.config; + const {axis_x_categories} = this.config; - return categories[i] ?? i; + return axis_x_categories?.[i] ?? i; }, }; diff --git a/src/module/browser.ts b/src/module/browser.ts index 33455e9bf..c3cce5a52 100644 --- a/src/module/browser.ts +++ b/src/module/browser.ts @@ -14,22 +14,43 @@ export { requestIdleCallback, cancelIdleCallback }; -const win = (() => { - const root = (typeof globalThis === "object" && globalThis !== null && globalThis.Object === Object && globalThis) || +/** + * Get global object + * @param {Array} g global object candidates + * @returns {object} window object + * @private + */ +function getGlobal() { + return (typeof globalThis === "object" && globalThis !== null && globalThis.Object === Object && globalThis) || (typeof global === "object" && global !== null && global.Object === Object && global) || - (typeof self === "object" && self !== null && self.Object === Object && self); - - return root || Function("return this")(); -})(); -/* eslint-enable no-new-func, no-undef */ + (typeof self === "object" && self !== null && self.Object === Object && self) || + Function("return this")(); +} -// fallback for non-supported environments -const hasRAF = typeof win.requestAnimationFrame === "function"; -const hasRIC = typeof win.requestIdleCallback === "function"; +/** + * Get fallback object + * @param {object} w global object + * @returns {Array} fallback object array + * @private + */ +function getFallback(w) { + const hasRAF = typeof w?.requestAnimationFrame === "function"; + const hasRIC = typeof w?.requestIdleCallback === "function"; -const requestAnimationFrame = hasRAF ? win.requestAnimationFrame : (cb => setTimeout(cb, 1)); -const cancelAnimationFrame = hasRAF ? win.cancelAnimationFrame : (id => clearTimeout(id)); -const requestIdleCallback = hasRIC ? win.requestIdleCallback : requestAnimationFrame; -const cancelIdleCallback = hasRIC ? win.cancelIdleCallback : cancelAnimationFrame; + return [ + hasRAF ? w.requestAnimationFrame : (cb => setTimeout(cb, 1)), + hasRAF ? w.cancelAnimationFrame : (id => clearTimeout(id)), + hasRIC ? w.requestIdleCallback : requestAnimationFrame, + hasRIC ? w.cancelIdleCallback : cancelAnimationFrame + ]; +} +const win = getGlobal(); const doc = win?.document; + +const [ + requestAnimationFrame, + cancelAnimationFrame, + requestIdleCallback, + cancelIdleCallback +] = getFallback(win); diff --git a/src/module/worker.ts b/src/module/worker.ts index 5d966bc8e..392036c06 100644 --- a/src/module/worker.ts +++ b/src/module/worker.ts @@ -35,6 +35,24 @@ function getObjectURL(fn: Function, depsFn?: Function[]): string { return window.URL.createObjectURL(blob[key]); } +/** + * Get WebWorker instance + * @param {string} src URL object as string + * @returns {object} WebWorker instance + * @private + */ +function getWorker(src) { + const worker = new window.Worker(src); + + // handle error + worker.onerror = function(e) { + // eslint-disable-next-line no-console + console.error ? console.error(e) : console.log(e); + }; + + return worker; +} + /** * Create and run on Web Worker * @param {boolean} useWorker Use Web Worker @@ -61,11 +79,15 @@ function getObjectURL(fn: Function, depsFn?: Function[]): string { export function runWorker( useWorker = true, fn: Function, callback: Function, depsFn?: Function[] ): Function { - let runFn; + let runFn = function(...args) { + const res = fn(...args); + + callback(res); + }; if (window.Worker && useWorker) { const src = getObjectURL(fn, depsFn); - const worker = new window.Worker(src); + const worker = getWorker(src); runFn = function(...args) { // trigger worker @@ -79,23 +101,11 @@ export function runWorker( return callback(e.data); }; - // handle error - worker.onerror = function(e) { - // eslint-disable-next-line no-console - console.error ? console.error(e) : console.log(e); - }; - // return new Promise((resolve, reject) => { // worker.onmessage = ({data}) => resolve(data); // worker.onerror = reject; // }); }; - } else { - runFn = function(...args) { - const res = fn(...args); - - callback(res); - }; } return runFn; diff --git a/test/api/category-spec.ts b/test/api/category-spec.ts index 33c9e3829..79e617f95 100644 --- a/test/api/category-spec.ts +++ b/test/api/category-spec.ts @@ -28,7 +28,7 @@ describe("API category", () => { }, axis: { x: { - type: "category" + type: "category", } } }; @@ -117,6 +117,30 @@ describe("API category", () => { expect(chart.$.tooltip.html()).to.be.empty; }); + it("set options: axis.x.categories=[]", () => { + args.axis.x.categories = []; + }); + + it("should return categories correctly.", () => { + const indexed = chart.data.values("loading").map((v, i) => i); + + expect(chart.categories()).to.be.deep.equal(indexed); + + }); + + it("should load without error when categories=null is given.", done => { + chart.load({ + columns: [ + ["data1", 20,30,33, 22] + ], + categories: null, + done() { + expect(true).to.be.ok; + done(); + } + }); + }); + it("set options", () => { args = { data: { diff --git a/test/module/module-spec.ts b/test/module/module-spec.ts index e52c67a77..b5f298162 100644 --- a/test/module/module-spec.ts +++ b/test/module/module-spec.ts @@ -7,7 +7,8 @@ import {expect} from "chai"; import sinon from "sinon"; import util from "../assets/util"; -import {window, document, requestAnimationFrame, cancelAnimationFrame, requestIdleCallback, cancelIdleCallback} from "../../src/module/browser"; +import {getGlobal, getFallback} from "../../src/module/browser"; +import {getWorker} from "../../src/module/worker"; describe("MODULE", function() { let chart; @@ -21,7 +22,7 @@ describe("MODULE", function() { chart.destroy(); }); - describe("Cache", () => { + describe("Cache", () => { before(() => { args = { data: { @@ -67,4 +68,57 @@ describe("MODULE", function() { expect(cache.get([rowData.id], true)).to.be.empty; }); }); + + describe("Browser", () => { + it("check global & fallback returns correctly when no default param is given.", () => { + // set nullish value for test + globalThis = global = self = null; + + const win = getGlobal(); + + expect(win.document).to.be.ok; + + // restore global + globalThis = global = self = window; + }); + + it("check fallback", done => { + const spy = sinon.spy(); + + const [ + requestAnimationFrame, + cancelAnimationFrame, + requestIdleCallback, + cancelIdleCallback + ] = getFallback(); + + const raf = requestAnimationFrame(spy); + const ric = requestIdleCallback(spy); + + expect(raf > 0).to.be.true; + expect(ric > 0).to.be.true; + + setTimeout(() => { + cancelAnimationFrame(raf); + cancelIdleCallback(ric); + + expect(spy.callCount).to.be.equal(2); + + done(); + }, 100); + }); + }); + + describe("Worker", () => { + it("check worker onerror handler call.", () => { + const worker = getWorker("test"); + const errorStub = sinon.stub(console, "error"); + + worker.onerror({e: {message: "ERR MSG"}}); + + expect(errorStub.called).to.be.true; + + errorStub.restore(); + }); + }); }); \ No newline at end of file