@embeddable.com/sdk-core
Version:
Core Embeddable SDK module responsible for web-components bundling and publishing.
168 lines (136 loc) • 5.28 kB
text/typescript
import path from "node:path";
import { describe, it, expect, vi, beforeEach } from "vitest";
import * as fs from "node:fs/promises";
import * as fsSync from "node:fs";
import process from "node:process";
import buildPackage from "./buildPackage";
import buildTypes from "./buildTypes";
import buildGlobalHooks from "./buildGlobalHooks";
import provideConfig from "./provideConfig";
import {
checkNodeVersion,
removeBuildSuccessFlag,
storeBuildSuccessFlag,
} from "./utils";
import { initLogger, logError } from "./logger";
import { ResolvedEmbeddableConfig } from "./defineConfig";
// ----------------- MOCKS ----------------- //
vi.mock("node:fs/promises");
vi.mock("node:fs");
// Mock buildTypes
vi.mock("./buildTypes", () => ({
default: vi.fn(),
}));
// Mock buildGlobalHooks
vi.mock("./buildGlobalHooks", () => ({
default: vi.fn(),
}));
// Mock provideConfig
vi.mock("./provideConfig", () => ({
default: vi.fn(),
}));
// Mock utils
vi.mock("./utils", async () => {
const actualUtils =
await vi.importActual<typeof import("./utils")>("./utils");
return {
...actualUtils,
checkNodeVersion: vi.fn(),
removeBuildSuccessFlag: vi.fn(),
storeBuildSuccessFlag: vi.fn(),
};
});
// Mock logger
vi.mock("./logger", () => ({
initLogger: vi.fn().mockResolvedValue(undefined),
logError: vi.fn().mockResolvedValue(undefined),
}));
// ----------------- TESTS ----------------- //
describe("buildPackage", () => {
// We'll create a sample plugin for testing
const mockPlugin = {
pluginName: "testPlugin",
validate: vi.fn(),
buildPackage: vi.fn(),
};
const rootDir = path.resolve("fake", "root");
const buildDir = path.resolve("fake", "build");
const distDir = path.resolve(rootDir, "dist");
// We'll also create a sample config
const config = {
client: {
rootDir,
buildDir,
},
plugins: [() => mockPlugin],
} as unknown as ResolvedEmbeddableConfig;
beforeEach(() => {
// Clear mocks before each test to avoid cross-test interference
vi.clearAllMocks();
// By default, provideConfig will return our sample config
vi.mocked(provideConfig).mockResolvedValue(config);
// Let's ensure fsSync.existsSync returns false by default
// (so that our prepare() logic tries to mkdir, etc.)
vi.spyOn(fsSync, "existsSync").mockReturnValue(false);
});
it("should call all main steps in a successful scenario", async () => {
await buildPackage();
// 1. Logger
expect(initLogger).toHaveBeenCalledWith("package");
// 2. checkNodeVersion and removeBuildSuccessFlag
expect(checkNodeVersion).toHaveBeenCalled();
expect(removeBuildSuccessFlag).toHaveBeenCalled();
// 3. provideConfig -> must be called, returns our config
expect(provideConfig).toHaveBeenCalled();
// 4. prepare() logic -> it checks if buildDir exists, removes if so, then mkdir
// Because we do cross-platform checks, we confirm the path used is distDir
expect(fsSync.existsSync).toHaveBeenCalledWith(distDir);
// Because it returned false, we do not remove
expect(fs.rm).toHaveBeenCalledTimes(0);
// Instead we create it
expect(fs.mkdir).toHaveBeenCalledWith(distDir);
// 5. buildTypes & buildGlobalHooks
expect(buildTypes).toHaveBeenCalledWith(config);
expect(buildGlobalHooks).toHaveBeenCalledWith(config);
// 6. Plugin calls
expect(mockPlugin.validate).toHaveBeenCalledWith(config);
expect(mockPlugin.buildPackage).toHaveBeenCalledWith(config);
// 7. storeBuildSuccessFlag
expect(storeBuildSuccessFlag).toHaveBeenCalled();
});
it("should remove and recreate buildDir if it already exists", async () => {
// This time let's simulate that buildDir already exists
vi.mocked(fsSync.existsSync).mockReturnValue(true);
await buildPackage();
// Because buildDir exists, prepare() should remove it
expect(fs.rm).toHaveBeenCalledWith(distDir, { recursive: true });
// Then re-create it
expect(fs.mkdir).toHaveBeenCalledWith(distDir);
});
it("should log an error, call logError, and exit(1) if an error occurs", async () => {
// We'll simulate an error in removeBuildSuccessFlag
const error = new Error("test error");
vi.mocked(removeBuildSuccessFlag).mockRejectedValueOnce(error);
// We want to check process.exit(1) is called
// but calling exit actually kills the process, so we mock it
const originalConsoleLog = console.log;
console.log = vi.fn();
const exitSpy = vi.spyOn(process, "exit").mockImplementation(() => {
throw new Error("process.exit called");
});
// We expect buildPackage to throw because exit is called
await expect(buildPackage()).rejects.toThrow("process.exit called");
// We also expect that logError has been called with the correct params
expect(logError).toHaveBeenCalledWith({
command: "package",
breadcrumbs: ["checkNodeVersion"], // because it fails after calling checkNodeVersion
error,
});
// And the error is printed
expect(console.log).toHaveBeenCalledWith(error);
// Finally, we confirm process.exit(1) was triggered
expect(process.exit).toHaveBeenCalledWith(1);
console.log = originalConsoleLog;
exitSpy.mockRestore();
});
});