/** * Copyright (c) 2026, Salesforce, Inc., * All rights reserved. * For full license text, see the LICENSE.txt file */ import { buildSchema } from "graphql"; import { describe, expect, it } from "vitest"; import { aggregatePath, connectionNodePath, connectionPath, createInputTypeName, filterTypeName, isValueWrapperType, mutationFieldPath, mutationRecordPath, nodePath, orderByTypeName, pageInfoPath, resolveScalarSelectionPath, updateInputTypeName, } from "../uiapi.js"; describe("uiapi", () => { describe("path constructors", () => { it("connectionPath builds the uiapi.query. path", () => { expect(connectionPath("Account")).toEqual(["uiapi", "query", "Account"]); }); it("nodePath descends through edges/node from the connection", () => { expect(nodePath("Account")).toEqual(["uiapi", "query", "Account", "edges", "node"]); }); it("connectionNodePath works on any connection path (incl. nested children)", () => { const nestedConnection = ["uiapi", "query", "Account", "edges", "node", "Cases"]; expect(connectionNodePath(nestedConnection)).toEqual([ "uiapi", "query", "Account", "edges", "node", "Cases", "edges", "node", ]); }); it("pageInfoPath sits at the connection root", () => { expect(pageInfoPath(["uiapi", "query", "Account"])).toEqual([ "uiapi", "query", "Account", "pageInfo", ]); }); it("mutationFieldPath builds uiapi. for each mutation kind", () => { expect(mutationFieldPath("Account", "Create")).toEqual(["uiapi", "AccountCreate"]); expect(mutationFieldPath("Account", "Update")).toEqual(["uiapi", "AccountUpdate"]); expect(mutationFieldPath("Account", "Delete")).toEqual(["uiapi", "AccountDelete"]); }); it("mutationRecordPath descends into the Record selection scope", () => { expect(mutationRecordPath("Account", "Create")).toEqual(["uiapi", "AccountCreate", "Record"]); }); it("aggregatePath builds the aggregate root", () => { expect(aggregatePath("Account")).toEqual(["uiapi", "aggregate", "Account"]); }); }); describe("naming conventions", () => { it("filterTypeName / orderByTypeName / *InputTypeName follow Salesforce conventions", () => { expect(filterTypeName("Account")).toBe("Account_Filter"); expect(orderByTypeName("Account")).toBe("Account_OrderBy"); expect(createInputTypeName("Account")).toBe("AccountCreateInput"); expect(updateInputTypeName("Account")).toBe("AccountUpdateInput"); }); }); describe("isValueWrapperType", () => { const wrapperSchema = buildSchema(` type Query { any: Holder } type Holder { Id: ID! Name: StringValue Status: PicklistValue # A *Value-named type that does NOT have a value field — the # detection helper must reject this to avoid false positives. ImpostorValue: Impostor } type StringValue { value: String } type PicklistValue { value: String } type Impostor { other: String } `); it("detects StringValue and PicklistValue", () => { expect(isValueWrapperType(wrapperSchema, "StringValue")).toBe(true); expect(isValueWrapperType(wrapperSchema, "PicklistValue")).toBe(true); }); it("rejects *Value types that don't expose a `value` field", () => { expect(isValueWrapperType(wrapperSchema, "Impostor")).toBe(false); }); it("returns false for unknown / non-object types", () => { expect(isValueWrapperType(wrapperSchema, "DoesNotExist")).toBe(false); expect(isValueWrapperType(wrapperSchema, "ID")).toBe(false); }); }); describe("resolveScalarSelectionPath", () => { const sobjectSchema = buildSchema(` type Query { uiapi: UIAPI! } type UIAPI { query: RecordQuery! } type RecordQuery { Account: AccountConnection! } type AccountConnection { edges: [AccountEdge!]! } type AccountEdge { node: Account! } type Account { Id: ID! Name: StringValue Industry: StringValue Owner: OwnerUnion } union OwnerUnion = User | Group type User { Id: ID! } type Group { Id: ID! } type StringValue { value: String } `); const accountNodePath = ["uiapi", "query", "Account", "edges", "node"]; it("returns the leaf path for Id (no value wrapper)", () => { expect(resolveScalarSelectionPath(sobjectSchema, "query", accountNodePath, "Id")).toEqual([ "uiapi", "query", "Account", "edges", "node", "Id", ]); }); it("appends 'value' for value-wrapper fields", () => { expect(resolveScalarSelectionPath(sobjectSchema, "query", accountNodePath, "Name")).toEqual([ "uiapi", "query", "Account", "edges", "node", "Name", "value", ]); }); it("returns the field path for non-leaf, non-wrapper types", () => { // Owner is a union — the helper returns the bare path; callers that // need union expansion use selectDottedFieldPath from path-selection.ts. expect(resolveScalarSelectionPath(sobjectSchema, "query", accountNodePath, "Owner")).toEqual([ "uiapi", "query", "Account", "edges", "node", "Owner", ]); }); it("returns null when the field can't be resolved", () => { expect( resolveScalarSelectionPath(sobjectSchema, "query", accountNodePath, "NotARealField"), ).toBeNull(); }); }); });