@embeddable.com/sdk-core
Version:
Core Embeddable SDK module responsible for web-components bundling and publishing.
216 lines (195 loc) • 6.62 kB
text/typescript
// buildGlobalHooks.int.test.ts
import { describe, it, expect, beforeEach, vi } from "vitest";
import * as fs from "node:fs/promises";
import * as fsSync from "node:fs";
import * as vite from "vite";
import {
getGlobalHooksMeta,
getComponentLibraryConfig,
getContentHash,
} from "@embeddable.com/sdk-utils";
import buildGlobalHooks from "../src/buildGlobalHooks";
import type { ResolvedEmbeddableConfig } from "./defineConfig";
import path from "node:path";
const rootDir = path.resolve("fake", "root");
const buildDir = path.resolve("fake", "build");
const srcDir = path.resolve("fake", "src");
const lifecycleFile = path.resolve("fake", "root", "embeddable.lifecycle.ts");
const themFile = path.resolve("fake", "root", "embeddable.theme.ts");
// Potential partial mocks, or we can let some logic run real
vi.mock("node:fs/promises");
vi.mock("node:fs");
vi.mock("vite");
vi.mock("@embeddable.com/sdk-utils", async () => {
const actual = await vi.importActual<
typeof import("@embeddable.com/sdk-utils")
>("@embeddable.com/sdk-utils");
return {
...actual,
getGlobalHooksMeta: vi.fn(),
getComponentLibraryConfig: vi.fn(),
getContentHash: vi.fn(),
};
});
describe("buildGlobalHooks (Integration Tests)", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("builds aggregator and lifecycle for multiple libraries, then saves meta", async () => {
// We pretend that the lifecycle file does exist
vi.spyOn(fsSync, "existsSync").mockImplementation((p: any) => {
// We'll say yes for lifecycle + local theme
if (p === lifecycleFile) return true;
if (p === themFile) return true;
return false;
});
// aggregator template or any read
(fs.readFile as any).mockResolvedValueOnce(
"{{LIBRARY_THEME_IMPORTS}}{{LOCAL_THEME_IMPORT}}",
);
// Suppose subsequent reads for the entry files also return "some content"
(fs.readFile as any).mockResolvedValue("some content");
// We hash them all
(getContentHash as any).mockReturnValue("123abc");
// Suppose vite.build calls are successful
(vite.build as any).mockResolvedValue(undefined);
// For each library
(getComponentLibraryConfig as any).mockImplementation((cfg: any) => ({
libraryName: cfg.name,
}));
(getGlobalHooksMeta as any)
// aggregator calls for library #1
.mockResolvedValueOnce({
themeProvider: "libA-theme.js",
lifecycleHooks: ["libA-lifecycle.js"],
})
// aggregator calls for library #2
.mockResolvedValueOnce({
themeProvider: "libB-theme.js",
lifecycleHooks: [],
})
// lifecycle calls for library #1
.mockResolvedValueOnce({
themeProvider: "libA-theme.js",
lifecycleHooks: ["libA-lifecycle.js"],
})
// lifecycle calls for library #2
.mockResolvedValueOnce({
themeProvider: "libB-theme.js",
lifecycleHooks: ["libB-lifecycle.js"],
});
const ctx: ResolvedEmbeddableConfig = {
client: {
srcDir,
buildDir,
rootDir,
lifecycleHooksFile: lifecycleFile,
customizationFile: themFile,
componentLibraries: [{ name: "libA" }, { name: "libB" }],
},
core: {
templatesDir: "/fake/templates",
},
dev: {
watch: false, // so we do hashing, no watchers
},
} as any;
await buildGlobalHooks(ctx);
// aggregator => built with "embeddable-theme-123abc"
expect(vite.build).toHaveBeenCalledWith(
expect.objectContaining({
build: expect.objectContaining({
lib: expect.objectContaining({
entry: expect.stringContaining("embeddableThemeHook.js"),
fileName: "embeddable-theme-123abc",
}),
}),
}),
);
// We also expect the lifecycle for the repo => "embeddable.lifecycle.ts"
expect(vite.build).toHaveBeenCalledWith(
expect.objectContaining({
build: expect.objectContaining({
lib: expect.objectContaining({
entry: lifecycleFile,
fileName: "embeddable-lifecycle", // or with hash if code does that
}),
}),
}),
);
// library #1 => has "libA-lifecycle.js"
// library #2 => has "libB-lifecycle.js"
// so we expect them to build also
expect(vite.build).toHaveBeenCalledWith(
expect.objectContaining({
build: expect.objectContaining({
lib: expect.objectContaining({
entry: path.resolve(
rootDir,
"node_modules",
"libA",
"dist",
"libA-lifecycle.js",
),
}),
}),
}),
);
expect(vite.build).toHaveBeenCalledWith(
expect.objectContaining({
build: expect.objectContaining({
lib: expect.objectContaining({
entry: path.resolve(
rootDir,
"node_modules",
"libB",
"dist",
"libB-lifecycle.js",
),
}),
}),
}),
);
// You might also check the final "saveGlobalHooksMeta" by reading the file or checking fsSync calls
// ...
});
it("skips aggregator if no library has themeProvider, no local theme", async () => {
vi.spyOn(fsSync, "existsSync").mockImplementation((p: any) => false);
(fs.readFile as any).mockResolvedValueOnce("{{LIBRARY_THEME_IMPORTS}}");
(getContentHash as any).mockReturnValue("someHash");
(vite.build as any).mockResolvedValue(undefined);
(getGlobalHooksMeta as any)
// aggregator calls:
.mockResolvedValueOnce({ themeProvider: null, lifecycleHooks: [] })
// lifecycle calls:
.mockResolvedValueOnce({ themeProvider: null, lifecycleHooks: [] });
(getComponentLibraryConfig as any).mockImplementation((cfg: any) => ({
libraryName: cfg.name,
}));
const ctx: ResolvedEmbeddableConfig = {
client: {
srcDir,
buildDir,
rootDir,
lifecycleHooksFile: lifecycleFile,
customizationFile: themFile,
componentLibraries: [{ name: "libA" }],
},
core: {
templatesDir: "/fake/templates",
},
dev: { watch: false },
} as any;
await buildGlobalHooks(ctx);
// aggregator not built
expect(vite.build).not.toHaveBeenCalledWith(
expect.objectContaining({
build: expect.objectContaining({
lib: expect.objectContaining({
entry: expect.stringContaining("embeddableThemeHook.js"),
}),
}),
}),
);
});
});