// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: MIT import { getBorderRadius, rgbToHex, indentation, generatePathNodeSnippet, getBorderWidthAndColor, generateTextSnippet, } from "../backend/utils/property-parsing"; import { expect, test } from "vitest"; const testJson = require("./figma_output.json"); export function findNodeById(obj: any, targetId: string): any { if (Array.isArray(obj)) { for (const item of obj) { const result = findNodeById(item, targetId); if (result) { return result; } } } else if (typeof obj === "object" && obj !== null) { if (obj.id === targetId) { return obj; } for (const key in obj) { const result = findNodeById(obj[key], targetId); if (result) { return result; } } } return null; } function findNodeByName(obj: any, targetName: string): any { if (Array.isArray(obj)) { for (const item of obj) { const result = findNodeByName(item, targetName); if (result) { return result; } } } else if (typeof obj === "object" && obj !== null) { if (obj.name === targetName) { return obj; } for (const key in obj) { const result = findNodeByName(obj[key], targetName); if (result) { return result; } } } return null; } // The JSON in the file for border radius is different to the API object // the runtime plugin uses. This converts the test JSON to match the API object. // This isn't a great solution, but test options are limited for Figma right now. function processCornerRadii(json: any): any { if (json.rectangleCornerRadii && Array.isArray(json.rectangleCornerRadii)) { const [ topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius, ] = json.rectangleCornerRadii; return { ...json, cornerRadius: Symbol(), topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius, }; } return json; } function processVectorNode(json: any): any { // You can expand this as needed for your test expectations if (json.type === "VECTOR") { return { ...json, vectorPaths: json.vectorPaths || [], strokes: json.strokes || [], strokeWeight: json.strokeWeight ?? 1, exportAsync: async () => ``, }; } return json; } // Convert test JSON to match the API object. function processTextNode(json: any): any { if (json.type === "TEXT" && json.style) { return { ...json, characters: json.characters, fontName: { family: json.style.fontFamily, style: json.style.fontStyle, }, fontSize: json.style.fontSize, fontWeight: json.style.fontWeight, }; } return json; } test("converts rgb to hex #ffffff", () => { const color = rgbToHex({ r: 1, g: 1, b: 1, a: 1 }); expect(color).toBe("#ffffff"); }); test("converts rgb to hex floating #ffffff", () => { const color = rgbToHex({ r: 1.0, g: 1.0, b: 1.0, a: 1.0 }); expect(color).toBe("#ffffff"); }); test("converts rgb to hex #000000", () => { const color = rgbToHex({ r: 0, g: 0, b: 0, a: 1 }); expect(color).toBe("#000000"); }); test("converts rgb to hex floating #000000", () => { const color = rgbToHex({ r: 0.0, g: 0.0, b: 0.0, a: 1.0 }); expect(color).toBe("#000000"); }); test(" No border radius", async () => { const jsonNode = findNodeByName( testJson, "rectangle no corner radius test", ); expect(jsonNode).not.toBeNull(); const snippet = await getBorderRadius(jsonNode, false); expect(snippet).toBe(null); }); test("Single border radius", async () => { const jsonNode = findNodeByName(testJson, "border-test 1"); expect(jsonNode).not.toBeNull(); const snippet = await getBorderRadius(jsonNode, false); expect(snippet).toBe(`${indentation}border-radius: 55px;`); }); test("Multiple border radius", async () => { const jsonNode = findNodeByName(testJson, "border-test 2"); expect(jsonNode).not.toBeNull(); const convertToApiJson = processCornerRadii(jsonNode); const snippet = await getBorderRadius(convertToApiJson, false); const expectedSnippet = `${indentation}border-top-left-radius: 50px;\n${indentation}border-top-right-radius: 28px;\n${indentation}border-bottom-right-radius: 30.343px;`; expect(snippet).toBe(expectedSnippet); }); test("Border width and color", async () => { const jsonNode = findNodeByName(testJson, "stroke test 2"); expect(jsonNode).not.toBeNull(); const snippet = await getBorderWidthAndColor(jsonNode, false); const expectedSnippet = [ `${indentation}border-width: 10.455px;`, `${indentation}border-color: #5c53dc;`, ]; expect(snippet).toStrictEqual(expectedSnippet); }); test("Text node", async () => { const jsonNode = findNodeByName(testJson, "Monthly"); expect(jsonNode).not.toBeNull(); const convertToApiJson = processTextNode(jsonNode); const snippet = await generateTextSnippet(convertToApiJson, false); const expectedSnippet = `monthly := Text {\n${indentation}text: "Monthly";\n${indentation}color: #896fff;\n${indentation}font-family: "Roboto";\n${indentation}font-size: 12px;\n${indentation}font-weight: 400;\n}`; expect(snippet).toBe(expectedSnippet); }); test("Vector node", async () => { const jsonNode = findNodeByName(testJson, "vector test"); expect(jsonNode).not.toBeNull(); const convertToApiJson = processVectorNode(jsonNode); const snippet = await generatePathNodeSnippet(convertToApiJson, false); const expectedSnippet = `vector-test := Path { ${indentation}commands: "M10 10L90 90"; ${indentation}fill: #2e5adf; ${indentation}stroke: #000000; ${indentation}stroke-width: 2.5px; }`; expect(snippet).toBe(expectedSnippet); });