mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-07 19:30:07 -05:00
8137de3c80
Add hub-backed feedback record actions and UI flows under Unify so workspaces can list and manage feedback records from a dedicated drawer and table experience. Made-with: Cursor
178 lines
6.0 KiB
TypeScript
178 lines
6.0 KiB
TypeScript
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
import { createFeedbackRecord, createFeedbackRecordsBatch, listFeedbackRecords } from "./service";
|
|
import type { FeedbackRecordCreateParams } from "./types";
|
|
|
|
vi.mock("@formbricks/logger", () => ({
|
|
logger: { warn: vi.fn(), error: vi.fn(), info: vi.fn() },
|
|
}));
|
|
|
|
vi.mock("./hub-client", () => ({
|
|
getHubClient: vi.fn(),
|
|
}));
|
|
|
|
const { getHubClient } = await import("./hub-client");
|
|
|
|
const sampleInput: FeedbackRecordCreateParams = {
|
|
field_id: "el-1",
|
|
field_type: "rating",
|
|
source_type: "formbricks_survey",
|
|
source_id: "survey-1",
|
|
source_name: "Test Survey",
|
|
field_label: "Question?",
|
|
value_number: 5,
|
|
collected_at: "2026-02-24T10:00:00.000Z",
|
|
};
|
|
|
|
describe("hub service", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("createFeedbackRecord", () => {
|
|
test("returns error result when getHubClient returns null", async () => {
|
|
vi.mocked(getHubClient).mockReturnValue(null);
|
|
|
|
const result = await createFeedbackRecord(sampleInput);
|
|
|
|
expect(result.data).toBeNull();
|
|
expect(result.error).toMatchObject({
|
|
status: 0,
|
|
message: "HUB_API_KEY is not set; Hub integration is disabled.",
|
|
});
|
|
});
|
|
|
|
test("returns data when client.create succeeds", async () => {
|
|
const created = { id: "hub-1", ...sampleInput };
|
|
vi.mocked(getHubClient).mockReturnValue({
|
|
feedbackRecords: { create: vi.fn().mockResolvedValue(created) },
|
|
} as any);
|
|
|
|
const result = await createFeedbackRecord(sampleInput);
|
|
|
|
expect(result.error).toBeNull();
|
|
expect(result.data).toEqual(created);
|
|
});
|
|
|
|
test("returns error result when client.create throws", async () => {
|
|
vi.mocked(getHubClient).mockReturnValue({
|
|
feedbackRecords: { create: vi.fn().mockRejectedValue(new Error("Network error")) },
|
|
} as any);
|
|
|
|
const result = await createFeedbackRecord(sampleInput);
|
|
|
|
expect(result.data).toBeNull();
|
|
expect(result.error).toMatchObject({ message: "Network error" });
|
|
});
|
|
});
|
|
|
|
describe("listFeedbackRecords", () => {
|
|
test("returns error result when getHubClient returns null", async () => {
|
|
vi.mocked(getHubClient).mockReturnValue(null);
|
|
|
|
const result = await listFeedbackRecords({ tenant_id: "env-1" });
|
|
|
|
expect(result.data).toBeNull();
|
|
expect(result.error).toMatchObject({
|
|
status: 0,
|
|
message: "HUB_API_KEY is not set; Hub integration is disabled.",
|
|
});
|
|
});
|
|
|
|
test("returns data when client.list succeeds", async () => {
|
|
const listResponse = {
|
|
data: [{ id: "rec-1", field_id: "el-1", field_type: "rating" }],
|
|
total: 1,
|
|
limit: 50,
|
|
offset: 0,
|
|
};
|
|
vi.mocked(getHubClient).mockReturnValue({
|
|
feedbackRecords: { list: vi.fn().mockResolvedValue(listResponse) },
|
|
} as any);
|
|
|
|
const result = await listFeedbackRecords({ tenant_id: "env-1", limit: 50, offset: 0 });
|
|
|
|
expect(result.error).toBeNull();
|
|
expect(result.data).toEqual(listResponse);
|
|
});
|
|
|
|
test("returns error result when client.list throws", async () => {
|
|
vi.mocked(getHubClient).mockReturnValue({
|
|
feedbackRecords: { list: vi.fn().mockRejectedValue(new Error("Network error")) },
|
|
} as any);
|
|
|
|
const result = await listFeedbackRecords({ tenant_id: "env-1" });
|
|
|
|
expect(result.data).toBeNull();
|
|
expect(result.error).toMatchObject({ status: 0, message: "Network error" });
|
|
});
|
|
|
|
test("returns data when called without params", async () => {
|
|
const listResponse = { data: [], total: 0, limit: 50, offset: 0 };
|
|
const listFn = vi.fn().mockResolvedValue(listResponse);
|
|
vi.mocked(getHubClient).mockReturnValue({
|
|
feedbackRecords: { list: listFn },
|
|
} as any);
|
|
|
|
const result = await listFeedbackRecords();
|
|
|
|
expect(result.error).toBeNull();
|
|
expect(result.data).toEqual(listResponse);
|
|
expect(listFn).toHaveBeenCalledWith(undefined);
|
|
});
|
|
});
|
|
|
|
describe("createFeedbackRecordsBatch", () => {
|
|
test("returns all errors when getHubClient returns null", async () => {
|
|
vi.mocked(getHubClient).mockReturnValue(null);
|
|
|
|
const result = await createFeedbackRecordsBatch([sampleInput, { ...sampleInput, field_id: "el-2" }]);
|
|
|
|
expect(result.results).toHaveLength(2);
|
|
result.results.forEach((r) => {
|
|
expect(r.data).toBeNull();
|
|
expect(r.error?.message).toContain("HUB_API_KEY is not set");
|
|
});
|
|
});
|
|
|
|
test("returns results per input when client creates succeed", async () => {
|
|
vi.mocked(getHubClient).mockReturnValue({
|
|
feedbackRecords: {
|
|
create: vi
|
|
.fn()
|
|
.mockImplementation((input: FeedbackRecordCreateParams) =>
|
|
Promise.resolve({ id: `hub-${input.field_id}`, ...input })
|
|
),
|
|
},
|
|
} as any);
|
|
|
|
const inputs = [sampleInput, { ...sampleInput, field_id: "el-2" }];
|
|
const result = await createFeedbackRecordsBatch(inputs);
|
|
|
|
expect(result.results).toHaveLength(2);
|
|
expect(result.results[0].data).toMatchObject({ field_id: "el-1" });
|
|
expect(result.results[0].error).toBeNull();
|
|
expect(result.results[1].data).toMatchObject({ field_id: "el-2" });
|
|
expect(result.results[1].error).toBeNull();
|
|
});
|
|
|
|
test("returns mixed results when some creates fail", async () => {
|
|
const create = vi
|
|
.fn()
|
|
.mockResolvedValueOnce({ id: "hub-1", ...sampleInput })
|
|
.mockRejectedValueOnce(new Error("Rate limited"));
|
|
vi.mocked(getHubClient).mockReturnValue({
|
|
feedbackRecords: { create },
|
|
} as any);
|
|
|
|
const inputs = [sampleInput, { ...sampleInput, field_id: "el-2" }];
|
|
const result = await createFeedbackRecordsBatch(inputs);
|
|
|
|
expect(result.results).toHaveLength(2);
|
|
expect(result.results[0].data).not.toBeNull();
|
|
expect(result.results[0].error).toBeNull();
|
|
expect(result.results[1].data).toBeNull();
|
|
expect(result.results[1].error).toMatchObject({ message: "Rate limited" });
|
|
});
|
|
});
|
|
});
|