diff --git a/src/domain/apple/interfaceBuilderGraph.test.ts b/src/domain/apple/interfaceBuilderGraph.test.ts index fb1c0b9ad..bfa2c71de 100644 --- a/src/domain/apple/interfaceBuilderGraph.test.ts +++ b/src/domain/apple/interfaceBuilderGraph.test.ts @@ -552,3 +552,32 @@ it("names the archive once per evidence record", () => { evidence.filter(({ description }) => description.includes(relativePath)), ).toEqual([]); }); + +it("states each object's class, label, and ID once", () => { + const { objects } = parseInterfaceBuilderRecords({ + "com.apple.ibtool.document.objects": { + "7": { + customClass: "NSButton", + label: "Start", + objectID: "7", + nibValues: { NSContents: "Start" }, + }, + button: { class: "UIButton", title: "Build", objectID: "authored" }, + }, + }); + expect(objects).toEqual([ + expect.objectContaining({ + id: "7", + class_name: "NSButton", + name: "Start", + attributes: { nibValues: { NSContents: "Start" } }, + }), + expect.objectContaining({ + id: "button", + class_name: "UIButton", + name: "Build", + // Values that differ from the canonical fields stay as authored. + attributes: { class: "UIButton", title: "Build", objectID: "authored" }, + }), + ]); +}); diff --git a/src/domain/apple/interfaceBuilderKeyedArchive.ts b/src/domain/apple/interfaceBuilderKeyedArchive.ts index 51b446b52..c0b7f3507 100644 --- a/src/domain/apple/interfaceBuilderKeyedArchive.ts +++ b/src/domain/apple/interfaceBuilderKeyedArchive.ts @@ -71,13 +71,23 @@ export const parseInterfaceBuilderRecords = ( attributes["name"], className, ); + // class_name, name, and id already carry these values; repeating them in + // attributes doubled them on every graph node. + const carried = new Set(); + if (attributes["customClass"] === className) carried.add("customClass"); + if (attributes["label"] === label) carried.add("label"); + if (attributes["objectID"] === id) carried.add("objectID"); objects.push( objectNode.parse({ id, kind: classifyObject(className), class_name: className, name: label ?? id, - attributes: jsonSafeRecord(attributes), + attributes: jsonSafeRecord( + Object.fromEntries( + Object.entries(attributes).filter(([key]) => !carried.has(key)), + ), + ), }), ); }