diff --git a/packages/xarc-subapp/.eslintrc.js b/packages/xarc-subapp/.eslintrc.js index bbb4f0e07..17fa680b8 100644 --- a/packages/xarc-subapp/.eslintrc.js +++ b/packages/xarc-subapp/.eslintrc.js @@ -1,4 +1,10 @@ const { eslintRcNodeTypeScript } = require("@xarc/module-dev"); module.exports = { - extends: eslintRcNodeTypeScript + extends: eslintRcNodeTypeScript, + plugins: ["jsdoc"], + rules: { + // disable the rule for all files + "@typescript-eslint/explicit-module-boundary-types": "off", + "jsdoc/require-param-type": "off" + } }; diff --git a/packages/xarc-subapp/package.json b/packages/xarc-subapp/package.json index ea30509fb..94ea206a2 100644 --- a/packages/xarc-subapp/package.json +++ b/packages/xarc-subapp/package.json @@ -49,6 +49,7 @@ "@babel/cli": "^7.12.1", "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/chai": "^4.2.14", + "@types/chai-as-promised": "^7.1.3", "@types/mocha": "^8.2.0", "@types/node": "^14.14.16", "@types/sinon": "^9.0.10", @@ -60,10 +61,13 @@ "babel-plugin-transform-remove-strict-mode": "^0.0.2", "babel-preset-minify": "^0.5.1", "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", "eslint": "^7.16.0", "eslint-config-walmart": "^2.2.1", "eslint-plugin-filenames": "^1.1.0", - "eslint-plugin-jsdoc": "^30.7.9", + "eslint-plugin-jsdoc": "^32.0.2", + "jsdom": "^16.4.0", + "jsdom-global": "^3.0.2", "mocha": "^8.2.1", "nyc": "^15.1.0", "sinon": "^9.2.2", diff --git a/packages/xarc-subapp/src/browser/client-render-pipeline.ts b/packages/xarc-subapp/src/browser/client-render-pipeline.ts index db0f562b4..d57637411 100644 --- a/packages/xarc-subapp/src/browser/client-render-pipeline.ts +++ b/packages/xarc-subapp/src/browser/client-render-pipeline.ts @@ -1,7 +1,7 @@ // // -import { SubAppCSRData, SubAppFeatureResult } from "../subapp/types"; +import { SubAppCSRData, SubAppDef, SubAppFeatureResult } from "../subapp/types"; import { envHooks } from "../subapp/envhooks"; import { SubAppRenderPipeline } from "../subapp/subapp-render-pipeline"; import { ClientFrameworkLib } from "../subapp/client-framework-lib"; @@ -20,7 +20,8 @@ export class ClientRenderPipeline implements Partial { this.csrData = csrData; this.framework = this._getSubApp()._frameworkFactory() as ClientFrameworkLib; } - + // TODO: '_reload' is defined but never used + // eslint-disable-next-line async start(_reload?: boolean): Promise { this.startPrepare(); await this.waitForPrepare(); @@ -54,15 +55,18 @@ export class ClientRenderPipeline implements Partial { this.framework.startSubAppSync(this.csrData, this); } - _getSubApp() { + _getSubApp(): SubAppDef { const container = envHooks.getContainer(); const subapp = container.get(this.subAppName); return subapp; } + // eslint-disable-next-line _mount(_: any): any { // } + + // eslint-disable-next-line _unmount(_: any): any { // } diff --git a/packages/xarc-subapp/src/browser/xarc-subapp-v2.ts b/packages/xarc-subapp/src/browser/xarc-subapp-v2.ts index 396dd7fc2..8f7fc9c0e 100644 --- a/packages/xarc-subapp/src/browser/xarc-subapp-v2.ts +++ b/packages/xarc-subapp/src/browser/xarc-subapp-v2.ts @@ -72,9 +72,11 @@ export function xarcV2Client( // // empty place holders for CDN mapping // + // eslint-disable-next-line cdnInit(_: any) { // }, + // eslint-disable-next-line cdnUpdate(_: any) { // }, diff --git a/packages/xarc-subapp/src/node/index.ts b/packages/xarc-subapp/src/node/index.ts index 6cb2ab14c..cc49606a1 100644 --- a/packages/xarc-subapp/src/node/index.ts +++ b/packages/xarc-subapp/src/node/index.ts @@ -39,6 +39,10 @@ export function getContainer(): SubAppContainer { return CONTAINER; } +export function _clearContainer(): void { + CONTAINER = undefined; +} + export function _setupEnvHooks() { if (!envHooks.getContainer) { envHooks.getContainer = getContainer; @@ -56,7 +60,8 @@ const serverOverrideMethods: Partial = { /** * Server side render pipeline factory - * @param params + * @param params pipeline factory params + * @returns subapp sever render pipeline object */ _pipelineFactory(params: PipelineFactoryParams) { const { ssrData } = params; diff --git a/packages/xarc-subapp/src/node/init-v2.ts b/packages/xarc-subapp/src/node/init-v2.ts index 6885b4c26..399c144bc 100644 --- a/packages/xarc-subapp/src/node/init-v2.ts +++ b/packages/xarc-subapp/src/node/init-v2.ts @@ -143,7 +143,7 @@ function initializeStaticAssets(props: InitProps) { runtimeJsScripts, mainJsScripts, allCssLinks, - randomId: Crypto.randomBytes(8).toString("base64") + randomId: Crypto.randomBytes(8).toString("base64") // eslint-disable-line }; } diff --git a/packages/xarc-subapp/src/node/load-v2.ts b/packages/xarc-subapp/src/node/load-v2.ts index 67a928455..8efb727dd 100644 --- a/packages/xarc-subapp/src/node/load-v2.ts +++ b/packages/xarc-subapp/src/node/load-v2.ts @@ -21,6 +21,7 @@ import { getContainer } from "./index"; * @param _setupContext setup context * @param param1 props object */ +// eslint-disable-next-line export function loadSubApp(_setupContext: any, { props: setupProps }): any { // name="Header" // async=true diff --git a/packages/xarc-subapp/src/node/render-page.ts b/packages/xarc-subapp/src/node/render-page.ts index f752c0970..5b99075a9 100644 --- a/packages/xarc-subapp/src/node/render-page.ts +++ b/packages/xarc-subapp/src/node/render-page.ts @@ -85,7 +85,7 @@ ${body.end} /** * Render index.html with subapps - * + * @params RenderOptions * @returns Promise */ async render(options: RenderOptions): Promise { diff --git a/packages/xarc-subapp/src/node/server-render-pipeline.ts b/packages/xarc-subapp/src/node/server-render-pipeline.ts index 6dc83b799..f806aa5b8 100644 --- a/packages/xarc-subapp/src/node/server-render-pipeline.ts +++ b/packages/xarc-subapp/src/node/server-render-pipeline.ts @@ -48,6 +48,7 @@ export class SubAppServerRenderPipeline implements SubAppRenderPipeline { this.framework = data.subapp._frameworkFactory(); } + // eslint-disable-next-line async start(_reload?: boolean): Promise { throw new Error("SubAppServerRenderPipeline doesn't handle start"); } diff --git a/packages/xarc-subapp/src/node/start-v2.ts b/packages/xarc-subapp/src/node/start-v2.ts index 794ba0ec5..ecd1b2d65 100644 --- a/packages/xarc-subapp/src/node/start-v2.ts +++ b/packages/xarc-subapp/src/node/start-v2.ts @@ -1,6 +1,10 @@ import { SSR_PIPELINES } from "./utils"; import { SubAppRenderPipeline } from "../subapp"; +/** To get start subapp object + * + * @returns an object with process method which can start subapp process + */ export function startSubApp(): any { return { process(context): string { diff --git a/packages/xarc-subapp/src/node/utils.ts b/packages/xarc-subapp/src/node/utils.ts index 66fdad5ca..38d3430b5 100644 --- a/packages/xarc-subapp/src/node/utils.ts +++ b/packages/xarc-subapp/src/node/utils.ts @@ -10,6 +10,7 @@ const NONCE_SIZE = 16; * Load CDN map * * @param cdnMap CDN map + * @returns cdn Map, if not found, return undefined */ export function loadCdnMap(cdnMap: string): any { const fullPath = Path.isAbsolute(cdnMap) @@ -23,6 +24,13 @@ export function loadCdnMap(cdnMap: string): any { } } +/** + * get + * + * @param file - string to wrap + * @param data - string go before fragment + * @returns if found, return request's path name, if not, return false + */ export function mapCdn(file: string, data: Record): string | boolean { if (data) { const reqBase = Path.posix.basename(file); @@ -52,13 +60,22 @@ export function wrapStringFragment(fragment: string, prefix = "", postfix = ""): return ""; } +// eslint-disable-next-line export function nonceGenerator(_?: string): string { const token = Crypto.randomBytes(NONCE_SIZE).toString("base64"); // drop "==" at the end - // eslint-disable no-magic-numbers + // eslint-disable-next-line return token.substr(0, token.length - 2); } +/** + * generate nonce token + * + * @param token init props + * @param fallback nonce fallback + * @param tag nonce token tag or nonce generator tag + * @returns an object with generated string token and nonce info + */ export function generateNonce( token: Partial<{ props: InitProps }>, fallback: NonceInfo = null, @@ -115,6 +132,7 @@ export const SSR_PIPELINES = Symbol("subapp-ssr-pipelines"); * Stringify a JSON object and replace some tags to avoid XSS: * - `` => `</script>` + * * @param obj - object to stringify * @returns JSON string of object */ diff --git a/packages/xarc-subapp/src/node/webpack-stats.ts b/packages/xarc-subapp/src/node/webpack-stats.ts index 267f74670..ee4fda3c9 100644 --- a/packages/xarc-subapp/src/node/webpack-stats.ts +++ b/packages/xarc-subapp/src/node/webpack-stats.ts @@ -17,7 +17,7 @@ export class WebpackStats { * - `WEBPACK_ENV` defined - load from `.etmp` dir * - otherwise - load from `dist/server` dir * - * @remarks will look for the data under dir by env `XARC_CWD` or `process.cwd()` + * '@remarks' will look for the data under dir by env `XARC_CWD` or `process.cwd()` * */ load() { @@ -26,12 +26,18 @@ export class WebpackStats { this._stats = JSON.parse(Fs.readFileSync(statsFile).toString()); } - /** names of all chunks */ + /** names of all chunks + * + * @returns an array of chuck names of assets + */ get allChunkNames() { return Object.keys(this._stats.assetsByChunkName); } - /** The raw stats data as loaded */ + /** The raw stats data as loaded + * + * @returns webpack status object + */ get stats() { return this._stats; } diff --git a/packages/xarc-subapp/test/spec/browser/index.spec.ts b/packages/xarc-subapp/test/spec/browser/index.spec.ts new file mode 100644 index 000000000..4ac3b03d8 --- /dev/null +++ b/packages/xarc-subapp/test/spec/browser/index.spec.ts @@ -0,0 +1,72 @@ +import "jsdom-global/register"; +import { + declareSubApp, + getContainer, + _setupEnvHooks, + IS_BROWSER +} from "../../../src/browser/index"; +import { envHooks } from "../../../src/subapp/index"; +import { describe, it } from "mocha"; +import { expect } from "chai"; + +describe("browser index", () => { + afterEach(() => { + (window as any)._subapps = undefined; + }); + + describe("getContainer", () => { + it("should getContainer create a new subapp container at first time", () => { + const container = getContainer(); + expect(container).is.an("object"); + + const container2 = getContainer(); + expect(container).equal(container2); + }); + }); + + describe("_setupEnvHooks", () => { + it("should _setupEnvHooks set getContainer to envHooks", () => { + _setupEnvHooks(); + expect(envHooks.getContainer).equal(getContainer); + + const getCon = envHooks.getContainer; + _setupEnvHooks(); + expect(envHooks.getContainer).equal(getCon); + }); + }); + + describe("declareSubApp", () => { + it("should add the subapp into container", async () => { + const container = getContainer(); + + const subapp = declareSubApp({ + name: "test", + getModule: () => import("../../blah") + }); + expect(container.getNames()).contains("test"); + expect(container.get("test")).to.equal(subapp); + expect(subapp._module).to.equal(undefined); + + const mod = await subapp._getModule(); + + expect(subapp._module).to.equal(mod); + expect(mod.subapp.Component()).to.equal("hello"); // eslint-disable-line + }); + }); + + describe("IS_BROWSER", () => { + it("should IS_BROWSER be true", () => { + expect(IS_BROWSER).true; // eslint-disable-line + }); + }); + + it("serverOverrideMethods exist", () => { + getContainer(); + const subapp = declareSubApp({ + name: "test", + getModule: () => import("../../blah") + }); + + expect(subapp._start).to.be.a("function"); + }); +}); diff --git a/packages/xarc-subapp/test/spec/browser/webpack4-jsonp.spec.ts b/packages/xarc-subapp/test/spec/browser/webpack4-jsonp.spec.ts new file mode 100644 index 000000000..2cf0cb830 --- /dev/null +++ b/packages/xarc-subapp/test/spec/browser/webpack4-jsonp.spec.ts @@ -0,0 +1,31 @@ +import { webpack4JsonP } from "../../../src/browser/webpack4-jsonp"; +import { describe, it } from "mocha"; +import { expect } from "chai"; + +const mockWindow: any = {}; +describe("webpack4JsonP", () => { + it("should webpack4JsonP set __webpack_get_script_src__ method on window", () => { + webpack4JsonP(mockWindow); + expect(mockWindow.__webpack_get_script_src__).to.be.a("function"); + }); + + it("should __webpack_get_script_src__ return original src when CDN is enabled", () => { + mockWindow.xarcV2 = { + cdnMap: src => "cdnMap" + src + }; + + expect(mockWindow.__webpack_get_script_src__("chunkId", "publicPath", "/test/src")).equal( + "cdnMap/test/src" + ); + }); + + it("should __webpack_get_script_src__ return original src when CDN is disabled", () => { + mockWindow.xarcV2 = { + cdnMap: str => undefined // eslint-disable-line + }; + + expect(mockWindow.__webpack_get_script_src__("chunkId", "publicPath", "/test/src")).equal( + "/test/src" + ); + }); +}); diff --git a/packages/xarc-subapp/test/spec/browser/xarc-cdn-map.spec.ts b/packages/xarc-subapp/test/spec/browser/xarc-cdn-map.spec.ts new file mode 100644 index 000000000..d56f6f95b --- /dev/null +++ b/packages/xarc-subapp/test/spec/browser/xarc-cdn-map.spec.ts @@ -0,0 +1,28 @@ +import { xarcCdnMap } from "../../../src/browser/xarc-cdn-map"; +import { xarcV2Client } from "../../../src/browser/xarc-subapp-v2"; + +import { describe, it } from "mocha"; +import { expect } from "chai"; +require("jsdom-global")("", { url: "https://localhost/" }); // eslint-disable-line + +const mockWindow = Object.assign({}, window); +describe("xarcCdnMap", () => { + it("should xarcCdnMap return undefined when window not having xarcV2 attribute", () => { + expect(xarcCdnMap(mockWindow)).undefined; // eslint-disable-line + }); + + it("should xarcCdnMap add subapp2 global attribute and method on window object", () => { + xarcV2Client(mockWindow); + xarcCdnMap(mockWindow); + const xarcV2 = (mockWindow as any).xarcV2; + expect(xarcV2).to.be.an("object"); + expect(xarcV2.cdnInit).to.be.a("function"); + expect(xarcV2.cdnUpdate).to.be.a("function"); + expect(xarcV2.cdnMap).to.be.a("function"); + expect((mockWindow as any)._wml.cdn.map).equal(xarcV2.cdnMap); + expect((mockWindow as any)._wml.cdn.update).equal(xarcV2.cdnUpdate); + expect((mockWindow as any)._wml.cdn.md) + .equal(xarcV2.rt.md) + .eql({}); + }); +}); diff --git a/packages/xarc-subapp/test/spec/browser/xarc-subapp-v2.spec.ts b/packages/xarc-subapp/test/spec/browser/xarc-subapp-v2.spec.ts new file mode 100644 index 000000000..828422a2e --- /dev/null +++ b/packages/xarc-subapp/test/spec/browser/xarc-subapp-v2.spec.ts @@ -0,0 +1,40 @@ +// eslint-disable max-statements +import { xarcV2Client } from "../../../src/browser/xarc-subapp-v2"; +import { describe, it } from "mocha"; +import { expect } from "chai"; +require("jsdom-global")("", { url: "https://localhost/" }); // eslint-disable-line + +const mockWindow = Object.assign({}, window); +let xarcV2; +describe("xarcV2Client", () => { + before(() => { + xarcV2Client(mockWindow); + xarcV2 = (mockWindow as any).xarcV2; + }); + it("should xarcV2Client add attributes on window object", () => { + expect(xarcV2).to.be.an("object"); + expect(xarcV2.IS_BROWSER).true; // eslint-disable-line + expect(xarcV2.HAS_WINDOW).true; // eslint-disable-line + expect(xarcV2.version).equal(2000000); // eslint-disable-line + expect(xarcV2.rt).eql({ + instId: 1, + subApps: {}, + onLoadStart: {}, + started: false, + md: {} + }); + }); + + it("should xarcV2Client add methods on window object", () => { + expect(xarcV2.cdnInit).to.be.a("function"); + expect(xarcV2.cdnUpdate).to.be.a("function"); + expect(xarcV2.getOnLoadStart).to.be.a("function"); + expect(xarcV2.cdnMap).to.be.a("function"); + expect(xarcV2.addOnLoadStart).to.be.a("function"); + expect(xarcV2.startSubAppOnLoad).to.be.a("function"); + expect(xarcV2._start).to.be.a("function"); + expect(xarcV2.start).to.be.a("function"); + expect(xarcV2.dyn).to.be.a("function"); + expect(xarcV2.debug).to.be.a("function"); + }); +}); diff --git a/packages/xarc-subapp/test/spec/browser/xarcv2.spec.ts b/packages/xarc-subapp/test/spec/browser/xarcv2.spec.ts new file mode 100644 index 000000000..ae1ffd95e --- /dev/null +++ b/packages/xarc-subapp/test/spec/browser/xarcv2.spec.ts @@ -0,0 +1,11 @@ +import { xarcV2 } from "../../../src/browser/xarcv2"; +import { describe, it } from "mocha"; +import { expect } from "chai"; + +describe("xarcV2", () => { + it("should xarcV2 be a function", () => { + expect(xarcV2).to.be.a("object"); + expect(xarcV2.debug).to.be.a("function"); + expect(xarcV2.debug()).equal(undefined); + }); +}); diff --git a/packages/xarc-subapp/test/spec/node/index.spec.ts b/packages/xarc-subapp/test/spec/node/index.spec.ts index 28ebce0cc..7f9649edd 100644 --- a/packages/xarc-subapp/test/spec/node/index.spec.ts +++ b/packages/xarc-subapp/test/spec/node/index.spec.ts @@ -1,8 +1,44 @@ -import { declareSubApp, getContainer } from "../../../src/node/index"; +import "jsdom-global/register"; +import { + declareSubApp, + getContainer, + _clearContainer, + _setupEnvHooks, + IS_BROWSER, + refreshAllSubApps2 +} from "../../../src/node/index"; +import { envHooks } from "../../../src/subapp/index"; import { describe, it } from "mocha"; import { expect } from "chai"; +import sinon from "sinon"; + +describe("node index", () => { + before(() => { + envHooks.getContainer = undefined; + }); + + afterEach(() => { + _clearContainer(); + _setupEnvHooks(); + }); + + it("should getContainer create a new subapp container at first time", () => { + const container = getContainer(); + expect(container).is.an("object"); + + const container2 = getContainer(); + expect(container).equal(container2); + }); + + it("should _setupEnvHooks set getContainer to envHooks", () => { + _setupEnvHooks(); + expect(envHooks.getContainer).equal(getContainer); + + const getCon = envHooks.getContainer; + _setupEnvHooks(); + expect(envHooks.getContainer).equal(getCon); + }); -describe("declareSubApp", () => { it("should add the subapp into container", async () => { const container = getContainer(); @@ -19,4 +55,46 @@ describe("declareSubApp", () => { expect(subapp._module).to.equal(mod); expect(mod.subapp.Component()).to.equal("hello"); // eslint-disable-line }); + + it("should IS_BROWSER be false", () => { + expect(IS_BROWSER).false; // eslint-disable-line + }); + + it("should refreshAllSubApps2 fresh all the subapp2 components", () => { + const container1 = getContainer(); + + declareSubApp({ + name: "test1", + getModule: () => import("../../blah") + }); + + declareSubApp({ + name: "test2", + getModule: () => import("../../blah") + }); + + const spy1 = sinon.spy(container1, "getNames"); + const spy2 = sinon.spy(container1, "get"); + const spy3 = sinon.spy(container1, "updateReady"); + + refreshAllSubApps2(); + + expect(spy1.calledOnce).true; // eslint-disable-line + expect(spy2.calledTwice).true; // eslint-disable-line + expect(spy3.calledOnce).true; // eslint-disable-line + expect(spy1.calledBefore(spy2)).true; // eslint-disable-line + expect(spy2.calledBefore(spy3)).true; // eslint-disable-line + sinon.restore(); + }); + + it("serverOverrideMethods exist", () => { + getContainer(); + const subapp = declareSubApp({ + name: "test", + getModule: () => import("../../blah") + }); + + expect(subapp._start).to.be.a("function"); + expect(subapp._pipelineFactory).to.be.a("function"); + }); }); diff --git a/packages/xarc-subapp/test/spec/node/load-v2.spec.ts b/packages/xarc-subapp/test/spec/node/load-v2.spec.ts new file mode 100644 index 000000000..336221d75 --- /dev/null +++ b/packages/xarc-subapp/test/spec/node/load-v2.spec.ts @@ -0,0 +1,38 @@ +import { loadSubApp } from "../../../src/node/load-v2"; +import sinon from "sinon"; +import { expect } from "chai"; +import { describe, it } from "mocha"; +import { _clearContainer, declareSubApp, getContainer } from "../../../src/node"; + +describe("Test load-v2", () => { + beforeEach(() => { + _clearContainer(); + }); + + after(() => { + _clearContainer(); + }); + + it("shold loadSubApp return an object includes process function", () => { + const res = loadSubApp({}, { props: { name: "test" } }); + expect(res).to.be.an("object"); + expect(res.process).to.be.a("function"); + }); + + it("should process function calls subapp _start method", () => { + declareSubApp({ + name: "test-subapp1", + getModule: () => import("../../blah") + }); + + const res = loadSubApp({ user: { request: () => {} } }, { props: { name: "test-subapp1" } }); // eslint-disable-line + const subapp = getContainer().get("test-subapp1"); + subapp._start = sinon.spy(); + // eslint-disable-next-line + expect(res.process({ user: { request: () => {} } }, { props: { name: "test-subapp1" } })).eql( + undefined + ); + expect((subapp._start as any).calledOnce).true; // eslint-disable-line + sinon.restore(); + }); +}); diff --git a/packages/xarc-subapp/test/spec/subapp/client-render-pipeline.spec.ts b/packages/xarc-subapp/test/spec/subapp/client-render-pipeline.spec.ts new file mode 100644 index 000000000..3a91a19de --- /dev/null +++ b/packages/xarc-subapp/test/spec/subapp/client-render-pipeline.spec.ts @@ -0,0 +1,13 @@ +import { ClientRenderPipeline } from "../../../src/subapp/client-render-pipeline"; +import { describe, it } from "mocha"; +import { expect, assert } from "chai"; + +describe("ClientRenderPipeline", () => { + it("should ClientRenderPipeline constructor throw error when be called", () => { + let clientRP; + assert.throw(() => { + clientRP = new ClientRenderPipeline({ name: "pipeline" }); + }, "src/subapp/client-render-pipeline.ts should not be used by anything"); + expect(clientRP).equal(undefined); + }); +}); diff --git a/packages/xarc-subapp/test/spec/subapp/index.spec.ts b/packages/xarc-subapp/test/spec/subapp/index.spec.ts new file mode 100644 index 000000000..49ff5976a --- /dev/null +++ b/packages/xarc-subapp/test/spec/subapp/index.spec.ts @@ -0,0 +1,108 @@ +import { + declareSubApp, + getContainer, + _setupEnvHooks, + _clearContainer +} from "../../../src/node/index"; +import { describe, it } from "mocha"; +import sinon from "sinon"; +import chai from "chai"; +import chaiAsPromised from "chai-as-promised"; + +chai.use(chaiAsPromised); +const expect = chai.expect; +chai.should(); + +describe("declareSubApp", () => { + beforeEach(() => { + _clearContainer(); + _setupEnvHooks(); + }); + + it("should add the subapp into container", async () => { + const container = getContainer(); + + const subapp = declareSubApp({ + name: "test", + getModule: () => import("../../blah") + }); + expect(container.getNames()).contains("test"); + expect(container.get("test")).to.equal(subapp); + expect(subapp._module).to.equal(undefined); + + const mod = await subapp._getModule(); + + expect(subapp._module).to.equal(mod); + expect(mod.subapp.Component()).to.equal("hello"); // eslint-disable-line + }); + + it("should add the subapp with wantFeatures", () => { + const spy1 = sinon.spy(); + const spy2 = sinon.spy(); + const container = getContainer(); + const subapp = declareSubApp({ + name: "test", + getModule: () => import("../../blah"), + wantFeatures: [ + { id: "1", add: spy1 }, + { id: "2", add: spy2 } + ] + }); + expect(container.getNames()).contains("test"); + expect(container.get("test")).to.equal(subapp); + expect(subapp._module).to.equal(undefined); + + expect(spy1.calledOnce).true; // eslint-disable-line + expect(spy2.calledOnce).true; // eslint-disable-line + expect(spy1.calledBefore(spy2)).true; // eslint-disable-line + + sinon.restore(); + }); + + it("should add the subapp into container", async () => { + const container = getContainer(); + + const subapp = declareSubApp({ + name: "test", + getModule: "test" + } as any); + expect(container.getNames()).contains("test"); + expect(container.get("test")).to.equal(subapp); + expect(subapp._module).to.equal(undefined); + + expect(subapp._getModule()).to.be.an("promise"); + subapp._getModule().should.eventually.eql(subapp._getModule()); + }); + + it("should get empty object when calling _getExport without _module on subapp", async () => { + const subapp = declareSubApp({ + name: "test", + getModule: "test" + } as any); + + expect(subapp._module).to.equal(undefined); + expect(subapp._getExport()).eql({}); + }); + + it("should get empty object when calling _getExport without _module on subapp", async () => { + const subapp = declareSubApp({ + name: "test", + getModule: "test" + } as any); + + expect(subapp._module).to.equal(undefined); + expect(subapp._getExport()).eql({}); + }); + + it("should get named module when calling _getExport with resolve name", async () => { + const subapp = declareSubApp({ + name: "test", + getModule: "test" + } as any); + + subapp._module = { test: "Hello World" }; + subapp.resolveName = "test"; + + expect(subapp._getExport()).eql("Hello World"); + }); +}); diff --git a/packages/xarc-subapp/test/spec/subapp/subapp-ready.spec.ts b/packages/xarc-subapp/test/spec/subapp/subapp-ready.spec.ts new file mode 100644 index 000000000..6c818c20a --- /dev/null +++ b/packages/xarc-subapp/test/spec/subapp/subapp-ready.spec.ts @@ -0,0 +1,90 @@ +import { envHooks } from "../../../src/subapp/index"; +import { SubAppContainer } from "../../../src/subapp/types"; +import { isSubAppReady, subAppReady } from "../../../src/subapp/subapp-ready"; +import { declareSubApp, _setupEnvHooks, _clearContainer } from "../../../src/node/index"; +import { describe, it } from "mocha"; +import chai from "chai"; +import chaiAsPromised from "chai-as-promised"; +import sinon from "sinon"; + +chai.use(chaiAsPromised); +const expect = chai.expect; +chai.should(); + +describe("subapp-ready", () => { + beforeEach(() => { + _clearContainer(); + _setupEnvHooks(); + }); + + describe("isSubAppReady", () => { + it("should isSubAppReady return true", () => { + expect(isSubAppReady()).true; // eslint-disable-line + }); + }); + + describe("subAppReady", () => { + beforeEach(() => { + _clearContainer(); + _setupEnvHooks(); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should subAppReady when container is ready", async () => { + const res = subAppReady(false, [], 0); + expect(res).to.be.a("promise"); + subAppReady(false, [], 0).should.eventually.equal(Promise.resolve()); + }); + + it("should subAppReady return subappModules array when container is not ready", async () => { + const subappDef = declareSubApp({ + name: "test-subapp1", + getModule: () => import("../../blah") + }); + + const subappCon = new SubAppContainer({ test: subappDef }); + + sinon.stub(envHooks, "getContainer").returns({ + ...subappCon, + get: str => subappDef, // eslint-disable-line + declare: (str, def) => subappDef, // eslint-disable-line + isReady: () => false, + updateReady: () => {}, // eslint-disable-line + getNames: () => ["test-subapp1", "test-subapp2"] + }); + + const res = subAppReady(true, [], 0); + res.should.eventually.eql([]); + }); + + it("should subAppReady return subappModules array when container's declare count is not ready", async () => { + const subappDef = declareSubApp({ + name: "test-subapp1", + getModule: () => import("../../blah") + }); + + declareSubApp({ + name: "test-subapp2", + getModule: () => import("../../blah") + }); + + const subappCon = new SubAppContainer({ test: subappDef }); + + sinon.stub(envHooks, "getContainer").returns({ + ...subappCon, + declareCount: 1, + get: str => subappDef, // eslint-disable-line + declare: (str, def) => subappDef, // eslint-disable-line + isReady: () => false, + updateReady: () => {}, // eslint-disable-line + getNames: () => [] + }); + + const res = subAppReady(true, [], 0); + res.should.eventually.eql([]); + }); + }); +}); diff --git a/packages/xarc-subapp/test/spec/subapp/types.spec.ts b/packages/xarc-subapp/test/spec/subapp/types.spec.ts new file mode 100644 index 000000000..02221cda2 --- /dev/null +++ b/packages/xarc-subapp/test/spec/subapp/types.spec.ts @@ -0,0 +1,34 @@ +import { SubAppContainer } from "../../../src/subapp/types"; +import { declareSubApp } from "../../../src/node/index"; +import { describe, it } from "mocha"; +import { expect } from "chai"; + +describe("SubAppContainer", () => { + it("should SubAppContainer methods works as expected", () => { + const subappDef1 = declareSubApp({ + name: "test-subapp1", + getModule: () => import("../../blah") + }); + subappDef1._module = () => "test-module1"; + const container = new SubAppContainer({ + test1: subappDef1 + }); + + expect(container.get("test1")).eql(subappDef1); + expect(container.get("test2")).eql(undefined); + expect(container.getNames()).eql(["test1"]); + expect(container.isReady()).true; // eslint-disable-line + + const subappDef2 = declareSubApp({ + name: "test-subapp2", + getModule: () => import("../../blah") + }); + subappDef2._module = () => "test-module2"; + expect(container.declare("test2", subappDef2)).eql(subappDef2); + expect(container.get("test1")).eql(subappDef1); + expect(container.get("test2")).eql(subappDef2); + expect(container.getNames()).eql(["test1", "test2"]); + expect(container.getNames().length).eql(2); // eslint-disable-line + expect(container.isReady()).true; // eslint-disable-line + }); +});