feat: adding fullstory sdk package files
This commit is contained in:
196
tests/cli.test.ts
Normal file
196
tests/cli.test.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import { jest } from "@jest/globals";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
// Create mock functions
|
||||
const mockUploadAssets = jest.fn();
|
||||
const mockFullStoryUploader = jest.fn().mockImplementation(() => ({
|
||||
uploadAssets: mockUploadAssets,
|
||||
}));
|
||||
|
||||
// Mock modules before importing the CLI
|
||||
jest.mock("../src/fullstoryUploader", () => ({
|
||||
__esModule: true,
|
||||
default: mockFullStoryUploader,
|
||||
}));
|
||||
|
||||
// Mock dotenv
|
||||
jest.mock("dotenv", () => ({
|
||||
config: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("FullStory Asset Uploader CLI", () => {
|
||||
const testAssetMapPath = path.resolve(__dirname, "test-asset-map.json");
|
||||
const testApiKey = "test-api-key";
|
||||
|
||||
// Create a test asset map file before tests
|
||||
beforeAll(() => {
|
||||
const testAssetMap = {
|
||||
assets: [
|
||||
{ id: "asset1", path: "./asset1.js" },
|
||||
{ id: "asset2", path: "./asset2.css" },
|
||||
],
|
||||
};
|
||||
|
||||
fs.writeFileSync(testAssetMapPath, JSON.stringify(testAssetMap, null, 2));
|
||||
});
|
||||
|
||||
// Clean up after tests
|
||||
afterAll(() => {
|
||||
if (fs.existsSync(testAssetMapPath)) {
|
||||
fs.unlinkSync(testAssetMapPath);
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// Clear all mocks before each test
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Reset modules to ensure clean Commander instance for each test
|
||||
jest.resetModules();
|
||||
});
|
||||
|
||||
it("should exit with an error when missing required arguments", () => {
|
||||
// Mock console.error and process.exit
|
||||
const consoleSpy = jest
|
||||
.spyOn(console, "error")
|
||||
.mockImplementation(() => {});
|
||||
const exitSpy = jest.spyOn(process, "exit").mockImplementation((code) => {
|
||||
throw new Error(`process.exit(${code}) was called`);
|
||||
});
|
||||
|
||||
try {
|
||||
// Run CLI with no arguments
|
||||
require("../src/cli");
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
expect(error.message).toContain("process.exit(1)");
|
||||
}
|
||||
|
||||
// ✅ Ensure an error message was logged
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("error: required option"),
|
||||
);
|
||||
|
||||
// ✅ Ensure process.exit was called
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
|
||||
// Restore original implementations
|
||||
consoleSpy.mockRestore();
|
||||
exitSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should initialize FullStoryUploader and call uploadAssets with correct options", () => {
|
||||
// Mock process.argv
|
||||
const originalArgv = process.argv;
|
||||
process.argv = [
|
||||
"node",
|
||||
"cli.js",
|
||||
"--apiKey",
|
||||
testApiKey,
|
||||
"--assetMap",
|
||||
testAssetMapPath,
|
||||
];
|
||||
|
||||
try {
|
||||
// Import CLI module which will execute the program
|
||||
require("../src/cli");
|
||||
|
||||
// Verify FullStoryUploader was constructed with correct params
|
||||
expect(mockFullStoryUploader).toHaveBeenCalledWith({
|
||||
apiKey: testApiKey,
|
||||
assetMapPath: testAssetMapPath,
|
||||
});
|
||||
|
||||
// Verify uploadAssets was called
|
||||
expect(mockUploadAssets).toHaveBeenCalled();
|
||||
} finally {
|
||||
// Restore original process.argv
|
||||
process.argv = originalArgv;
|
||||
}
|
||||
});
|
||||
|
||||
it("should initialize FullStoryUploader with short flag options", () => {
|
||||
// Mock process.argv
|
||||
const originalArgv = process.argv;
|
||||
process.argv = ["node", "cli.js", "-k", testApiKey, "-a", testAssetMapPath];
|
||||
|
||||
try {
|
||||
// Import CLI module which will execute the program
|
||||
require("../src/cli");
|
||||
|
||||
// Verify FullStoryUploader was constructed with correct params
|
||||
expect(mockFullStoryUploader).toHaveBeenCalledWith({
|
||||
apiKey: testApiKey,
|
||||
assetMapPath: testAssetMapPath,
|
||||
});
|
||||
|
||||
// Verify uploadAssets was called
|
||||
expect(mockUploadAssets).toHaveBeenCalled();
|
||||
} finally {
|
||||
// Restore original process.argv
|
||||
process.argv = originalArgv;
|
||||
}
|
||||
});
|
||||
|
||||
it("should display version information with --version flag", () => {
|
||||
// ✅ Mock process.exit to prevent Jest from failing
|
||||
const exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {
|
||||
throw new Error("process.exit(0) was called");
|
||||
});
|
||||
|
||||
// ✅ Capture stdout output
|
||||
const originalWrite = process.stdout.write;
|
||||
let output = "";
|
||||
process.stdout.write = (chunk: any) => {
|
||||
output += chunk;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Mock process.argv
|
||||
const originalArgv = process.argv;
|
||||
process.argv = ["node", "cli.js", "--version"];
|
||||
|
||||
try {
|
||||
require("../src/cli");
|
||||
} catch (e) {
|
||||
// Ignore expected process.exit error
|
||||
const error = e as Error;
|
||||
expect(error.message).toContain("process.exit");
|
||||
}
|
||||
|
||||
// ✅ Ensure the version is in the output
|
||||
expect(output).toContain("0.0.1");
|
||||
|
||||
// Restore mocks and original argv
|
||||
process.stdout.write = originalWrite;
|
||||
process.argv = originalArgv;
|
||||
exitSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should display help information with --help flag", () => {
|
||||
// Mock console.log and process.exit
|
||||
const consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {});
|
||||
const exitSpy = jest.spyOn(process, "exit").mockImplementation((code) => {
|
||||
throw new Error(`process.exit(${code}) was called`);
|
||||
});
|
||||
|
||||
// Mock process.argv
|
||||
const originalArgv = process.argv;
|
||||
process.argv = ["node", "cli.js", "--help"];
|
||||
|
||||
try {
|
||||
// Import CLI module
|
||||
require("../src/cli");
|
||||
} catch (e) {
|
||||
// Expected error from process.exit()
|
||||
const error = e as Error;
|
||||
expect(error.message).toContain("process.exit");
|
||||
}
|
||||
|
||||
// Restore mocks and original argv
|
||||
consoleSpy.mockRestore();
|
||||
exitSpy.mockRestore();
|
||||
process.argv = originalArgv;
|
||||
});
|
||||
});
|
||||
77
tests/fullstoryUploader.test.ts
Normal file
77
tests/fullstoryUploader.test.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import FullStoryUploader from "../src/fullstoryUploader";
|
||||
import fs from "fs-extra";
|
||||
import axios from "axios";
|
||||
|
||||
// Mock fs-extra to simulate file existence and content reading
|
||||
jest.mock("fs-extra", () => ({
|
||||
existsSync: jest.fn(() => true),
|
||||
readFileSync: jest.fn(() => JSON.stringify({ assets: {}, version: "2" })),
|
||||
}));
|
||||
|
||||
// Mock axios to simulate API responses
|
||||
jest.mock("axios");
|
||||
|
||||
describe("FullStoryUploader", () => {
|
||||
const mockApiKey = "test-api-key";
|
||||
const mockAssetMapPath = "test/assets.json";
|
||||
|
||||
it("should upload assets successfully", async () => {
|
||||
// Mock successful API response
|
||||
(axios.post as jest.Mock).mockResolvedValue({ status: 200 });
|
||||
|
||||
const uploader = new FullStoryUploader({
|
||||
apiKey: mockApiKey,
|
||||
assetMapPath: mockAssetMapPath,
|
||||
});
|
||||
|
||||
const consoleSpy = jest.spyOn(console, "log").mockImplementation();
|
||||
await uploader.uploadAssets();
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("✅ Assets uploaded successfully!"),
|
||||
);
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should handle file not found error", async () => {
|
||||
// Simulate a missing file
|
||||
(fs.existsSync as jest.Mock).mockReturnValue(false);
|
||||
|
||||
const uploader = new FullStoryUploader({
|
||||
apiKey: mockApiKey,
|
||||
assetMapPath: "nonexistent.json",
|
||||
});
|
||||
|
||||
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
||||
await uploader.uploadAssets();
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("❌ Asset map file not found"),
|
||||
);
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should handle API errors", async () => {
|
||||
// Ensure the file exists so the API call runs
|
||||
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
||||
|
||||
// Simulate an API error
|
||||
const apiError = new Error("API error");
|
||||
(axios.post as jest.Mock).mockRejectedValue(apiError);
|
||||
|
||||
const uploader = new FullStoryUploader({
|
||||
apiKey: mockApiKey,
|
||||
assetMapPath: mockAssetMapPath,
|
||||
});
|
||||
|
||||
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
||||
await uploader.uploadAssets();
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("❌ Error uploading assets"),
|
||||
apiError, // This ensures we match the error object in the log
|
||||
);
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
70
tests/index.test.ts
Normal file
70
tests/index.test.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
import { jest } from '@jest/globals';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
// Create mock functions with explicit types
|
||||
const mockUploadAssets = jest.fn<() => Promise<{ success: boolean }>>().mockResolvedValue({ success: true });
|
||||
const mockFullStoryUploader = jest.fn().mockImplementation(() => ({
|
||||
uploadAssets: mockUploadAssets,
|
||||
}));
|
||||
|
||||
// Mock modules before importing the index
|
||||
jest.mock('../src/fullstoryUploader', () => ({
|
||||
__esModule: true,
|
||||
default: mockFullStoryUploader,
|
||||
}));
|
||||
|
||||
// Import the module under test
|
||||
import { uploadAssets } from '../src/index';
|
||||
|
||||
describe('Index Module', () => {
|
||||
const testAssetMapPath = path.resolve(__dirname, 'test-asset-map.json');
|
||||
const testApiKey = 'test-api-key';
|
||||
|
||||
beforeAll(() => {
|
||||
const testAssetMap = {
|
||||
assets: [
|
||||
{ id: 'asset1', path: './asset1.js' },
|
||||
{ id: 'asset2', path: './asset2.css' },
|
||||
],
|
||||
};
|
||||
|
||||
fs.writeFileSync(testAssetMapPath, JSON.stringify(testAssetMap, null, 2));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (fs.existsSync(testAssetMapPath)) {
|
||||
fs.unlinkSync(testAssetMapPath);
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should initialize FullStoryUploader with correct params and call uploadAssets', async () => {
|
||||
const result = await uploadAssets(testApiKey, testAssetMapPath);
|
||||
|
||||
expect(mockFullStoryUploader).toHaveBeenCalledWith({
|
||||
apiKey: testApiKey,
|
||||
assetMapPath: testAssetMapPath,
|
||||
});
|
||||
|
||||
expect(mockUploadAssets).toHaveBeenCalled();
|
||||
expect(result).toEqual({ success: true });
|
||||
});
|
||||
|
||||
it('should propagate errors from FullStoryUploader.uploadAssets', async () => {
|
||||
const testError = new Error('Upload failed');
|
||||
mockUploadAssets.mockRejectedValueOnce(testError);
|
||||
|
||||
await expect(uploadAssets(testApiKey, testAssetMapPath)).rejects.toThrow(testError);
|
||||
|
||||
expect(mockFullStoryUploader).toHaveBeenCalledWith({
|
||||
apiKey: testApiKey,
|
||||
assetMapPath: testAssetMapPath,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user