@embeddable.com/sdk-core
Version:
Core Embeddable SDK module responsible for web-components bundling and publishing.
263 lines (212 loc) • 8.1 kB
text/typescript
import * as fs from "node:fs/promises";
import {
checkNodeVersion,
getArgumentByKey,
storeBuildSuccessFlag,
checkBuildSuccess,
SUCCESS_FLAG_FILE,
hrtimeToISO8601,
getSDKVersions,
} from "./utils";
const startMock = {
succeed: vi.fn(),
fail: vi.fn(),
stop: vi.fn(),
};
const failMock = vi.fn();
// Mock for testing resolveLocalFileVersion
const mockPackageJson = {
version: "1.2.3",
};
vi.mock("ora", () => ({
default: () => ({
start: vi.fn().mockImplementation(() => startMock),
info: vi.fn(),
fail: failMock,
}),
}));
vi.mock("../package.json", () => ({
engines: {
node: "14.x",
},
}));
vi.mock("fs/promises", () => ({
readFile: vi.fn(),
writeFile: vi.fn(),
access: vi.fn(),
mkdir: vi.fn(),
unlink: vi.fn(),
}));
describe("utils", () => {
describe("checkNodeVersion", () => {
it("should return true if the node version is greater than 14", async () => {
vi.mocked(fs.readFile).mockResolvedValue(
JSON.stringify({
engines: {
node: "16.x",
},
}),
);
const result = await checkNodeVersion();
expect(result).toBe(true);
});
it("should fail if the node version is less than 16", async () => {
Object.defineProperty(process.versions, "node", {
value: "14.0",
configurable: true,
});
vi.mocked(fs.readFile).mockResolvedValue(
JSON.stringify({
engines: {
node: "16.x",
},
}),
);
vi.spyOn(process, "exit").mockImplementation(() => null as never);
const result = await checkNodeVersion();
expect(startMock.fail).toHaveBeenCalledWith(
"Node version 16.0 or higher is required. You are running 14.0.",
);
expect(result).toBe(undefined);
});
it("should throw an error if the package.json file cannot be read", async () => {
vi.spyOn(process, "exit").mockImplementation(() => null as never);
vi.mocked(fs.readFile).mockRejectedValue(
new Error("Failed to read package.json of core-sdk"),
);
await expect(checkNodeVersion()).rejects.toThrow(
"Failed to read package.json of core-sdk",
);
});
});
describe("getArgumentByKey", () => {
it("should return the value of the argument", () => {
process.argv = ["--email", "test@a.com", "--password", "123456"];
const result = getArgumentByKey("--email");
expect(result).toBe("test@a.com");
process.argv = ["-e", "test@a.com", "--password", "123456"];
const result2 = getArgumentByKey(["--email", "-e"]);
expect(result2).toBe("test@a.com");
});
});
describe("storeBuildSuccessFlag", () => {
it("should write a success flag file", async () => {
await storeBuildSuccessFlag();
expect(fs.writeFile).toHaveBeenCalledWith(SUCCESS_FLAG_FILE, "true");
});
it("should throw an error if the file write fails", async () => {
vi.mocked(fs.writeFile).mockImplementation(async () => {
throw new Error();
});
await expect(storeBuildSuccessFlag()).rejects.toThrow();
});
});
describe("checkBuildSuccess", () => {
it("should return true if the success flag file exists", async () => {
vi.mocked(fs.access).mockResolvedValue(undefined);
const result = await checkBuildSuccess();
expect(result).toBe(true);
});
it("should return false if the success flag file does not exist", async () => {
vi.mocked(fs.access).mockRejectedValue(undefined);
const result = await checkBuildSuccess();
expect(result).toBe(false);
});
});
describe("getSDKVersions", () => {
beforeEach(() => {
process.env.NODE_ENV = "local";
vi.resetAllMocks();
process.cwd = vi.fn().mockReturnValue("/test/path");
});
afterEach(() => {
process.env.NODE_ENV = "test";
});
it("should get versions from node_modules", async () => {
// Mock the readFile to return a version for the first package
vi.mocked(fs.readFile).mockResolvedValueOnce(
JSON.stringify(mockPackageJson),
);
const result = await getSDKVersions();
expect(result).toHaveProperty("@embeddable.com/core", "1.2.3");
});
it("should handle local file references", async () => {
// Mock package.json with local file references
const mockProjectPackageJson = {
dependencies: {
"@embeddable.com/core": "file:../packages/core",
},
};
// First readFile calls for node_modules fail
vi.mocked(fs.readFile)
.mockRejectedValueOnce(new Error("Not found")) // core
.mockRejectedValueOnce(new Error("Not found")) // react
.mockRejectedValueOnce(new Error("Not found")) // sdk-core
.mockRejectedValueOnce(new Error("Not found")) // sdk-react
.mockRejectedValueOnce(new Error("Not found")) // sdk-utils
// Then project package.json succeeds
.mockResolvedValueOnce(JSON.stringify(mockProjectPackageJson))
// Then the referenced package.json succeeds
.mockResolvedValueOnce(JSON.stringify({ version: "2.0.0" }));
const result = await getSDKVersions();
expect(result).toHaveProperty("@embeddable.com/core", "2.0.0");
});
it("should handle errors when resolving local file references", async () => {
// Mock console.warn to avoid test output noise
const consoleWarnSpy = vi
.spyOn(console, "warn")
.mockImplementation(() => {});
// Mock package.json with local file references
const mockProjectPackageJson = {
dependencies: {
"@embeddable.com/core": "file:../packages/core",
},
};
// First readFile calls for node_modules fail
vi.mocked(fs.readFile)
.mockRejectedValueOnce(new Error("Not found")) // core
.mockRejectedValueOnce(new Error("Not found")) // react
.mockRejectedValueOnce(new Error("Not found")) // sdk-core
.mockRejectedValueOnce(new Error("Not found")) // sdk-react
.mockRejectedValueOnce(new Error("Not found")) // sdk-utils
// Then project package.json succeeds
.mockResolvedValueOnce(JSON.stringify(mockProjectPackageJson))
// Then the referenced package.json fails
.mockRejectedValueOnce(new Error("Cannot read file"));
const result = await getSDKVersions();
expect(consoleWarnSpy).toHaveBeenCalled();
expect(result).toHaveProperty("@embeddable.com/core", "local-dev");
consoleWarnSpy.mockRestore();
});
it("should use fallback values in test environment when no versions found", async () => {
// Mock console.warn to avoid test output noise
const consoleWarnSpy = vi
.spyOn(console, "warn")
.mockImplementation(() => {});
// Mock all readFile calls to fail
vi.mocked(fs.readFile).mockRejectedValue(new Error("Not found"));
// Mock process.cwd to return a test-sdk path
process.cwd = vi.fn().mockReturnValue("/test/path/test-sdk");
const result = await getSDKVersions();
// Should have fallback values for all packages
expect(Object.keys(result).length).toBeGreaterThan(0);
expect(result["@embeddable.com/core"]).toBe("local-dev");
expect(result["@embeddable.com/sdk-core"]).toBe("local-dev");
consoleWarnSpy.mockRestore();
});
});
describe("hrtimeToISO8601", () => {
test.each([
{ input: [0, 0], expected: "PT0.000S" },
{ input: [75, 0], expected: "PT1M15.000S" }, // 1 minute and 15 seconds
{ input: [2, 500000000], expected: "PT2.500S" }, // 2.5 seconds
{ input: [0, 123456789], expected: "PT0.123S" }, // 0.123 seconds
{ input: [0, 500000000], expected: "PT0.500S" }, // 0.5 seconds
{ input: [1, 0], expected: "PT1.000S" }, // 1 second
{ input: null, expected: "" },
{ input: undefined, expected: "" },
])("converts hrtime $input to $expected", ({ input, expected }) => {
expect(hrtimeToISO8601(input)).toBe(expected);
});
});
});