@embeddable.com/sdk-core
Version:
Core Embeddable SDK module responsible for web-components bundling and publishing.
897 lines (763 loc) • 28.4 kB
text/typescript
import push, { buildArchive, EMBEDDABLE_FILES, sendBuild, sendBuildByApiKey } from "./push";
import provideConfig from "./provideConfig";
import { fileFromPath } from "formdata-node/file-from-path";
import * as path from "path";
import yazl from "yazl";
import * as fs from "node:fs/promises";
import * as fsSync from "node:fs";
import { findFiles } from "@embeddable.com/sdk-utils";
import { ResolvedEmbeddableConfig } from "./defineConfig";
import { vi, describe, it, expect, beforeEach } from "vitest";
import { checkBuildSuccess, checkNodeVersion, getArgumentByKey, shouldSkipModelCheck } from "./utils";
// @ts-ignore
import reportErrorToRollbar from "./rollbar.mjs";
import { server } from "../../../mocks/server";
import { http, HttpResponse } from "msw";
const warnMock = vi.fn();
const infoMock = {
info: vi.fn(),
succeed: vi.fn(),
fail: vi.fn(),
};
const startMock = {
succeed: vi.fn(),
info: () => infoMock,
fail: vi.fn(),
};
vi.mock("ora", () => ({
default: () => ({
start: vi.fn().mockReturnValue(startMock),
info: vi.fn(),
warn: warnMock,
}),
}));
vi.mock("./utils", () => ({
checkNodeVersion: vi.fn(),
checkBuildSuccess: vi.fn(),
getArgumentByKey: vi.fn(),
getSDKVersions: vi.fn(),
shouldSkipModelCheck: vi.fn().mockReturnValue(false),
}));
vi.mock("node:fs/promises", () => ({
writeFile: vi.fn(),
readFile: vi.fn(),
access: vi.fn(),
rm: vi.fn(),
mkdir: vi.fn(),
stat: vi.fn(),
appendFile: vi.fn(),
}));
vi.mock("node:fs", () => ({
createWriteStream: vi.fn(),
readdirSync: vi.fn().mockReturnValue([]),
statSync: vi.fn().mockReturnValue({ isFile: () => true }),
existsSync: vi.fn().mockReturnValue(true),
}));
vi.mock("./provideConfig", () => ({
default: vi.fn().mockResolvedValue({
client: {
rootDir: "rootDir",
buildDir: "buildDir",
archiveFile: "embeddable-build.zip",
},
}),
}));
vi.mock("./rollbar.mjs", () => ({
default: vi.fn(),
}));
vi.mock("@embeddable.com/sdk-utils", () => ({
findFiles: vi.fn(),
}));
const { zipRef } = vi.hoisted(() => ({
zipRef: { current: null as any },
}));
vi.mock("yazl", () => ({
default: {
ZipFile: vi.fn(function () { return zipRef.current; }),
},
}));
vi.mock("formdata-node/file-from-path", () => ({
fileFromPath: vi.fn().mockReturnValue(new Blob([new ArrayBuffer(8)])),
}));
const config = {
client: {
rootDir: "rootDir",
buildDir: "buildDir",
archiveFile: "embeddable-build.zip",
customCanvasCss: "src/custom-canvas.css",
},
pushBaseUrl: "http://localhost:3000",
previewBaseUrl: "http://localhost:3000",
pushComponents: true,
};
describe("push", () => {
const zipMock = {
addFile: vi.fn(),
end: vi.fn(),
outputStream: { pipe: vi.fn() },
};
beforeEach(() => {
vi.mocked(checkNodeVersion).mockResolvedValue(true);
vi.mocked(checkBuildSuccess).mockResolvedValue(true);
vi.mocked(getArgumentByKey).mockReturnValue(undefined);
vi.mocked(provideConfig).mockResolvedValue(
config as ResolvedEmbeddableConfig
);
vi.mocked(fs.access).mockResolvedValue(undefined);
vi.mocked(fs.readFile).mockImplementation(async () =>
Buffer.from(`{"access_token":"mocked-token"}`)
);
vi.mocked(fs.stat).mockResolvedValue({
size: 100,
} as any);
vi.mocked(findFiles).mockResolvedValue([["fileName", "filePath"]]);
vi.mocked(fileFromPath).mockReturnValue(
new Blob([new ArrayBuffer(8)]) as any
);
zipRef.current = zipMock;
vi.mocked(fsSync.readdirSync).mockReturnValue([]);
vi.mocked(fsSync.existsSync).mockReturnValue(true);
vi.mocked(fsSync.createWriteStream).mockReturnValue({
on: (event: string, cb: () => void) => {
cb();
},
end: vi.fn(),
} as any);
vi.spyOn(process, "exit").mockImplementation(() => null as never);
});
it("should push the build", async () => {
await push();
expect(provideConfig).toHaveBeenCalled();
expect(checkNodeVersion).toHaveBeenCalled();
expect(checkBuildSuccess).toHaveBeenCalled();
expect(fs.access).toHaveBeenCalledWith(config.client.buildDir);
expect(yazl.ZipFile).toHaveBeenCalled();
expect(fsSync.createWriteStream).toHaveBeenCalledWith(
config.client.archiveFile
);
expect(zipMock.outputStream.pipe).toHaveBeenCalled();
expect(zipMock.addFile).toHaveBeenCalledWith(
"src/custom-canvas.css", "global.css", expect.objectContaining({ compress: true })
);
expect(fsSync.readdirSync).toHaveBeenCalledWith(
"buildDir", expect.objectContaining({ recursive: true })
);
expect(zipMock.end).toHaveBeenCalled();
// after publishing the file gets removed
expect(fs.rm).toHaveBeenCalledWith(config.client.archiveFile);
expect(infoMock.info).toHaveBeenCalledWith(
"Publishing to mocked-workspace-name using http://localhost:3000/workspace/mocked-workspace-id..."
);
expect(infoMock.succeed).toHaveBeenCalledWith(
"Published to mocked-workspace-name using http://localhost:3000/workspace/mocked-workspace-id"
);
});
it("should fail if there are no workspaces", async () => {
vi.spyOn(console, "error").mockImplementation(() => undefined);
server.use(
http.get("**/workspace", () => {
return HttpResponse.json([]);
})
);
await push();
expect(startMock.fail).toHaveBeenCalledWith("No workspaces found");
expect(process.exit).toHaveBeenCalledWith(1);
expect(reportErrorToRollbar).toHaveBeenCalled();
});
it("should fail if the build is not successful", async () => {
vi.spyOn(console, "error").mockImplementation(() => undefined);
vi.mocked(checkBuildSuccess).mockResolvedValue(false);
await push();
expect(process.exit).toHaveBeenCalledWith(1);
expect(console.error).toHaveBeenCalledWith(
"Build failed or not completed. Please run `embeddable:build` first."
);
});
it("should push by api key provided in the arguments", async () => {
vi.mocked(getArgumentByKey).mockReturnValue("mocked-api-key");
Object.defineProperties(process, {
argv: {
value: [
"--api-key",
"mocked-api-key",
"--email",
"mocked-email@valid.com",
],
},
});
await push();
expect(startMock.succeed).toHaveBeenCalledWith("Published using API key");
});
describe("push configuration", () => {
it("should fail if pushModels, pushComponents, and pushEmbeddables are all disabled", async () => {
vi.spyOn(console, "error").mockImplementation(() => undefined);
vi.mocked(provideConfig).mockResolvedValue({
...config,
pushModels: false,
pushComponents: false,
pushEmbeddables: false,
} as ResolvedEmbeddableConfig);
await push();
expect(startMock.fail).toHaveBeenCalledWith(
"Cannot push: pushModels, pushComponents, and pushEmbeddables are all disabled"
);
expect(process.exit).toHaveBeenCalledWith(1);
});
it("should only include component files when pushModels is false", async () => {
const localZipMock = {
addFile: vi.fn(),
end: vi.fn(),
outputStream: { pipe: vi.fn() },
};
zipRef.current = localZipMock;
vi.mocked(provideConfig).mockResolvedValue({
...config,
pushModels: false,
pushComponents: true,
} as ResolvedEmbeddableConfig);
await push();
// Should read component build directory
expect(fsSync.readdirSync).toHaveBeenCalledWith(
"buildDir", expect.objectContaining({ recursive: true })
);
// Should include global.css and client context files
expect(localZipMock.addFile).toHaveBeenCalledWith(
expect.anything(), "global.css", expect.objectContaining({ compress: true })
);
});
});
describe("API key validation", () => {
it("should fail if API key is not provided", async () => {
vi.spyOn(console, "error").mockImplementation(() => undefined);
Object.defineProperties(process, {
argv: {
value: ["--api-key"],
},
});
vi.mocked(getArgumentByKey).mockReturnValue(undefined);
await push();
expect(startMock.fail).toHaveBeenCalledWith("No API key provided");
expect(process.exit).toHaveBeenCalledWith(1);
});
it("should fail if email is not provided with API key", async () => {
vi.spyOn(console, "error").mockImplementation(() => undefined);
Object.defineProperties(process, {
argv: {
value: ["--api-key", "some-key"],
},
});
vi.mocked(getArgumentByKey)
.mockReturnValueOnce("some-key") // API key
.mockReturnValueOnce(undefined); // Email
await push();
expect(startMock.fail).toHaveBeenCalledWith(
"Invalid email provided. Please provide a valid email using --email (-e) flag"
);
expect(process.exit).toHaveBeenCalledWith(1);
});
it("should fail if email is invalid", async () => {
vi.spyOn(console, "error").mockImplementation(() => undefined);
Object.defineProperties(process, {
argv: {
value: ["--api-key", "some-key", "--email", "invalid-email"],
},
});
vi.mocked(getArgumentByKey)
.mockReturnValueOnce("some-key") // API key
.mockReturnValueOnce("invalid-email"); // Invalid email
await push();
expect(startMock.fail).toHaveBeenCalledWith(
"Invalid email provided. Please provide a valid email using --email (-e) flag"
);
expect(process.exit).toHaveBeenCalledWith(1);
});
it("should accept optional message parameter", async () => {
Object.defineProperties(process, {
argv: {
value: [
"--api-key",
"some-key",
"--email",
"valid@email.com",
"--message",
"test message",
"--cube-version",
"v1.34",
],
},
});
vi.mocked(getArgumentByKey).mockImplementation((keysArg) => {
const key = Array.isArray(keysArg) ? keysArg[0] : keysArg;
if (key === "--api-key") return "some-key";
if (key === "--email") return "valid@email.com";
if (key === "--message") return "test message";
if (key === "--cube-version") return "v1.34";
return undefined;
});
await push();
expect(startMock.succeed).toHaveBeenCalledWith("Published using API key");
});
});
describe("error handling", () => {
beforeEach(() => {
// Reset all mocks to their default state
vi.mocked(getArgumentByKey).mockReturnValue(undefined);
Object.defineProperties(process, {
argv: {
value: [],
},
});
});
it("should fail if build directory does not exist", async () => {
vi.spyOn(console, "error").mockImplementation(() => undefined);
vi.mocked(fs.access).mockRejectedValue(new Error("No such directory"));
vi.mocked(provideConfig).mockResolvedValue(
config as ResolvedEmbeddableConfig
);
await push();
expect(console.error).toHaveBeenCalledWith(
"No embeddable build was produced."
);
expect(process.exit).toHaveBeenCalledWith(1);
});
it("should fail if token is not available", async () => {
vi.spyOn(console, "error").mockImplementation(() => undefined);
vi.mocked(fs.access).mockResolvedValue(undefined);
vi.mocked(fs.readFile).mockResolvedValue(Buffer.from("{}"));
vi.mocked(provideConfig).mockResolvedValue(
config as ResolvedEmbeddableConfig
);
await push();
expect(console.error).toHaveBeenCalledWith(
'Unauthorized. Please login using "npm run embeddable:login"'
);
expect(process.exit).toHaveBeenCalledWith(1);
});
it("should handle and report errors during push", async () => {
const testError = new Error("Test error");
vi.mocked(provideConfig).mockRejectedValue(testError);
vi.mocked(fs.access).mockResolvedValue(undefined);
vi.mocked(fs.readFile).mockImplementation(async () =>
Buffer.from(`{"access_token":"mocked-token"}`)
);
await push();
expect(reportErrorToRollbar).toHaveBeenCalledWith(testError);
expect(process.exit).toHaveBeenCalledWith(1);
});
});
describe("buildArchive", () => {
type MockZip = {
addFile: ReturnType<typeof vi.fn>;
end: ReturnType<typeof vi.fn>;
outputStream: { pipe: ReturnType<typeof vi.fn> };
};
let mockZipLocal: MockZip;
beforeEach(() => {
mockZipLocal = {
addFile: vi.fn(),
end: vi.fn(),
outputStream: { pipe: vi.fn() },
};
zipRef.current = mockZipLocal;
vi.mocked(fsSync.readdirSync).mockReturnValue([]);
vi.mocked(fsSync.existsSync).mockReturnValue(true);
vi.mocked(findFiles).mockResolvedValue([]);
});
it("should include all file types when both flags are true", async () => {
vi.mocked(findFiles)
.mockResolvedValueOnce([
["model1.cube.yml", "/path/to/model1.cube.yml"],
["model2.cube.yaml", "/path/to/model2.cube.yaml"],
])
.mockResolvedValueOnce([
["context1.sc.yml", "/path/to/context1.sc.yml"],
["context2.cc.yml", "/path/to/context2.cc.yml"],
]);
const testConfig = {
...config,
pushModels: true,
pushComponents: true,
client: {
...config.client,
srcDir: "/src",
},
} as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
// Should read component build directory
expect(fsSync.readdirSync).toHaveBeenCalledWith(
testConfig.client.buildDir,
expect.objectContaining({ recursive: true })
);
// Should include global.css
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
testConfig.client.customCanvasCss,
"global.css",
expect.objectContaining({ compress: true })
);
// Should include all model files
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
"/path/to/model1.cube.yml",
"model1.cube.yml",
expect.objectContaining({ compress: true })
);
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
"/path/to/model2.cube.yaml",
"model2.cube.yaml",
expect.objectContaining({ compress: true })
);
// Should include all preset files
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
"/path/to/context1.sc.yml",
"context1.sc.yml",
expect.objectContaining({ compress: true })
);
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
"/path/to/context2.cc.yml",
"context2.cc.yml",
expect.objectContaining({ compress: true })
);
});
it("should only include component files when pushModels is false", async () => {
const testConfig = {
...config,
pushModels: false,
pushComponents: true,
client: {
...config.client,
srcDir: "/src",
},
} as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
// Should read component build directory
expect(fsSync.readdirSync).toHaveBeenCalledWith(
testConfig.client.buildDir,
expect.objectContaining({ recursive: true })
);
// Should include global.css
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
testConfig.client.customCanvasCss,
"global.css",
expect.objectContaining({ compress: true })
);
// Should only find client context files
expect(findFiles).toHaveBeenCalledOnce();
});
it("should search in custom directories for model files", async () => {
const testConfig = {
...config,
pushModels: true,
pushComponents: true,
client: {
...config.client,
srcDir: "/src",
modelsSrc: "/custom/models/path",
presetsSrc: "/custom/presets/path",
},
} as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
expect(findFiles).toHaveBeenCalledWith(
"/custom/models/path",
expect.any(RegExp)
);
expect(findFiles).toHaveBeenCalledWith(
"/custom/presets/path",
expect.any(RegExp)
);
});
it("should add directory files to zip with correct relative paths", async () => {
vi.mocked(fsSync.readdirSync).mockReturnValue([
"index.js",
"components/widget.js",
] as any);
vi.mocked(fsSync.statSync).mockReturnValue({ isFile: () => true } as any);
const testConfig = {
...config,
pushModels: false,
pushComponents: true,
client: {
...config.client,
srcDir: "/src",
},
} as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
path.join("buildDir", "index.js"),
"index.js",
expect.objectContaining({ compress: true })
);
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
path.join("buildDir", "components/widget.js"),
"components/widget.js",
expect.objectContaining({ compress: true })
);
});
it("should skip directories when adding directory contents to zip", async () => {
vi.mocked(fsSync.readdirSync).mockReturnValue(["components"] as any);
vi.mocked(fsSync.statSync).mockReturnValue({ isFile: () => false } as any);
const testConfig = {
...config,
pushModels: false,
pushComponents: true,
client: {
...config.client,
srcDir: "/src",
},
} as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
const addFileCalls = mockZipLocal.addFile.mock.calls;
const dirCall = addFileCalls.find(
(call: any) => call[1] === "components"
);
expect(dirCall).toBeUndefined();
});
it("should skip customCanvasCss when file does not exist", async () => {
vi.mocked(fsSync.existsSync).mockReturnValue(false);
const testConfig = {
...config,
pushModels: false,
pushComponents: true,
client: {
...config.client,
srcDir: "/src",
},
} as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
const addFileCalls = mockZipLocal.addFile.mock.calls;
const cssCall = addFileCalls.find(
(call: any) => call[1] === "global.css"
);
expect(cssCall).toBeUndefined();
});
it("should skip buildDir when pushComponents is false (even if it exists)", async () => {
vi.mocked(fsSync.existsSync).mockReturnValue(true);
const testConfig = {
...config,
pushModels: true,
pushComponents: false,
client: {
...config.client,
srcDir: "/src",
},
} as ResolvedEmbeddableConfig;
await expect(buildArchive(testConfig)).resolves.not.toThrow();
expect(fsSync.readdirSync).not.toHaveBeenCalled();
});
it("should use srcDir as fallback when modelsSrc/presetsSrc are not defined", async () => {
const testConfig = {
...config,
pushModels: true,
pushComponents: true,
client: {
...config.client,
srcDir: "/src",
modelsSrc: undefined,
presetsSrc: undefined,
},
} as unknown as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
expect(findFiles).toHaveBeenCalledWith("/src", expect.any(RegExp));
expect(findFiles).toHaveBeenCalledWith("/src", expect.any(RegExp));
});
it("should include embeddable files when pushEmbeddables is true", async () => {
vi.mocked(findFiles)
.mockResolvedValueOnce([]) // cube files
.mockResolvedValueOnce([]) // security context files
.mockResolvedValueOnce([]) // client context files
.mockResolvedValueOnce([
["dashboard.embeddable.yaml", "/src/dashboard.embeddable.yaml"],
["report.embeddable.yml", "/src/report.embeddable.yml"],
]); // embeddable files
const testConfig = {
...config,
pushModels: true,
pushComponents: true,
pushEmbeddables: true,
client: {
...config.client,
srcDir: "/src",
},
} as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
expect(findFiles).toHaveBeenCalledWith("/src", EMBEDDABLE_FILES);
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
"/src/dashboard.embeddable.yaml",
"dashboard.embeddable.yaml",
expect.objectContaining({ compress: true }),
);
expect(mockZipLocal.addFile).toHaveBeenCalledWith(
"/src/report.embeddable.yml",
"report.embeddable.yml",
expect.objectContaining({ compress: true }),
);
});
it("should not search for embeddable files when pushEmbeddables is false", async () => {
// Reset call history to avoid interference from previous tests
vi.mocked(findFiles).mockClear();
const testConfig = {
...config,
pushModels: true,
pushComponents: true,
pushEmbeddables: false,
client: {
...config.client,
srcDir: "/src",
},
} as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
// findFiles should have been called for models and components but NOT for embeddable files
const embeddableFilesCall = vi
.mocked(findFiles)
.mock.calls.find((call) => call[1] === EMBEDDABLE_FILES);
expect(embeddableFilesCall).toBeUndefined();
});
it("should exit when all three push flags are disabled", async () => {
vi.spyOn(process, "exit").mockImplementation(() => null as never);
const testConfig = {
...config,
pushModels: false,
pushComponents: false,
pushEmbeddables: false,
client: {
...config.client,
srcDir: "/src",
},
} as ResolvedEmbeddableConfig;
await buildArchive(testConfig);
expect(process.exit).toHaveBeenCalledWith(1);
});
});
describe("EMBEDDABLE_FILES", () => {
it("should match .embeddable.yaml files", () => {
expect(EMBEDDABLE_FILES.test("my-dashboard.embeddable.yaml")).toBe(true);
expect(EMBEDDABLE_FILES.test("src/dashboards/sales.embeddable.yaml")).toBe(
true,
);
});
it("should match .embeddable.yml files", () => {
expect(EMBEDDABLE_FILES.test("my-dashboard.embeddable.yml")).toBe(true);
expect(EMBEDDABLE_FILES.test("src/widgets/chart.embeddable.yml")).toBe(
true,
);
});
it("should not match unrelated yaml files", () => {
expect(EMBEDDABLE_FILES.test("config.yaml")).toBe(false);
expect(EMBEDDABLE_FILES.test("my-dashboard.cc.yaml")).toBe(false);
expect(EMBEDDABLE_FILES.test("my-dashboard.sc.yaml")).toBe(false);
});
it("should not match cube files", () => {
expect(EMBEDDABLE_FILES.test("my-model.cube.yaml")).toBe(false);
expect(EMBEDDABLE_FILES.test("my-model.cube.yml")).toBe(false);
});
it("should not match non-yaml extensions", () => {
expect(EMBEDDABLE_FILES.test("my-dashboard.embeddable.json")).toBe(false);
expect(EMBEDDABLE_FILES.test("my-dashboard.embeddable.ts")).toBe(false);
});
});
describe.each([
{
label: "sendBuild",
endpoint: "**/bundle/:workspaceId/upload",
invoke: (testConfig: ResolvedEmbeddableConfig, skipModelCheck?: boolean) =>
sendBuild(testConfig, { workspaceId: "test-workspace", token: "test-token", skipModelCheck }),
},
{
label: "sendBuildByApiKey",
endpoint: "**/bundle/upload",
invoke: (testConfig: ResolvedEmbeddableConfig, skipModelCheck?: boolean) =>
sendBuildByApiKey(testConfig, { apiKey: "test-api-key", email: "test@example.com", skipModelCheck }),
},
])("$label", ({ endpoint, invoke }) => {
async function captureMetadata(pushEmbeddables: boolean, skipModelCheck?: boolean) {
let capturedMetadata: Record<string, any> | undefined;
server.use(
http.post(endpoint, async ({ request }) => {
const formData = await request.formData();
const requestBlob = formData.get("request") as Blob;
capturedMetadata = JSON.parse(await requestBlob.text());
return HttpResponse.json({ bundleId: "mocked-bundle-id" });
}),
);
const testConfig = {
...config,
pushEmbeddables,
region: "us",
starterEmbeddables: {},
} as unknown as ResolvedEmbeddableConfig;
await invoke(testConfig, skipModelCheck);
return capturedMetadata;
}
it("should include pushEmbeddables in form data metadata when true", async () => {
const metadata = await captureMetadata(true);
expect(metadata?.pushEmbeddables).toBe(true);
});
it("should include pushEmbeddables in form data metadata when false", async () => {
const metadata = await captureMetadata(false);
expect(metadata?.pushEmbeddables).toBe(false);
});
it("should include skipModelCheck in form data metadata when true", async () => {
const metadata = await captureMetadata(true, true);
expect(metadata?.skipModelCheck).toBe(true);
});
it("should not include skipModelCheck in form data metadata when false", async () => {
const metadata = await captureMetadata(true, false);
expect(metadata?.skipModelCheck).toBeUndefined();
});
});
describe("sendBuild warning suppression", () => {
beforeEach(() => {
warnMock.mockClear();
});
async function invokeSendBuildWithWarnings(
flags: { pushModels?: boolean; pushComponents?: boolean; pushEmbeddables?: boolean },
warnings: string[],
) {
server.use(
http.post("**/bundle/:workspaceId/upload", () =>
HttpResponse.json({ bundleId: "mocked-bundle-id", warnings }),
),
);
const testConfig = {
...config,
pushModels: flags.pushModels ?? true,
pushComponents: flags.pushComponents ?? true,
pushEmbeddables: flags.pushEmbeddables ?? true,
region: "us",
starterEmbeddables: {},
} as unknown as ResolvedEmbeddableConfig;
await sendBuild(testConfig, { workspaceId: "test-workspace", token: "test-token" });
}
it("should suppress WARN-005 when pushEmbeddables is false", async () => {
await invokeSendBuildWithWarnings({ pushEmbeddables: false }, ["WARN-005: Embeddable upload disabled"]);
expect(warnMock).not.toHaveBeenCalled();
});
it("should suppress WARN-001 when pushModels is false", async () => {
await invokeSendBuildWithWarnings({ pushModels: false }, ["WARN-001: Model upload disabled"]);
expect(warnMock).not.toHaveBeenCalled();
});
it("should suppress WARN-003 when pushComponents is false", async () => {
await invokeSendBuildWithWarnings({ pushComponents: false }, ["WARN-003: Component upload disabled"]);
expect(warnMock).not.toHaveBeenCalled();
});
it("should still show unexpected warnings even when some flags are false", async () => {
await invokeSendBuildWithWarnings(
{ pushEmbeddables: false },
["WARN-005: Embeddable upload disabled", "WARN-999: Something unexpected"],
);
expect(warnMock).toHaveBeenCalledWith("WARN-999: Something unexpected");
});
it("should not suppress warnings with a code that only starts with a suppressed code", async () => {
await invokeSendBuildWithWarnings(
{ pushModels: false },
["WARN-0010: Some other warning"],
);
expect(warnMock).toHaveBeenCalledWith("WARN-0010: Some other warning");
});
it("should show all warnings when all flags are true", async () => {
await invokeSendBuildWithWarnings(
{ pushModels: true, pushComponents: true, pushEmbeddables: true },
["WARN-001: Model upload disabled"],
);
expect(warnMock).toHaveBeenCalledWith("WARN-001: Model upload disabled");
});
});
});