mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-12 08:51:11 +00:00
M3: .fig import: an empty overriddenSymbolID is no swap (#2206)
Tests on FigCraft's document for the fix that craft-fig carries. From storytold/figcraft#2208.
This commit is contained in:
1 parent
f0603ab239
commit
f28f29a84e
2 files changed
+104
No files matched your search
@@ -20,6 +20,8 @@ mod connector_label_tests;
|
||||
#[cfg(test)]
|
||||
mod deep_import_tests;
|
||||
#[cfg(test)]
|
||||
mod empty_swap_tests;
|
||||
#[cfg(test)]
|
||||
mod expansion_cost_tests;
|
||||
mod export;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
//! #2206: Figma writes the empty GUID (4294967295:4294967295) in `overriddenSymbolID` for "no
|
||||
//! swap". The instance keeps the main its `symbolData` names instead of becoming a frame.
|
||||
|
||||
use figcraft_doc::{Document, NodeId, NodeKind};
|
||||
|
||||
use super::kiwi::{Kv, Schema};
|
||||
use super::tests::{arr, canvas, g, o};
|
||||
use super::*;
|
||||
|
||||
type Fields = Vec<(&'static str, Kv<'static>)>;
|
||||
|
||||
/// The GUID Figma writes for "none".
|
||||
fn empty() -> Kv<'static> {
|
||||
o(vec![("sessionID", Kv::Uint(4_294_967_295)), ("localID", Kv::Uint(4_294_967_295))])
|
||||
}
|
||||
|
||||
/// A node under `parent` (the shared `node` with a position from the id).
|
||||
fn node(id: u64, parent: Option<u64>, ty: &'static str, name: &str, more: Fields) -> Kv<'static> {
|
||||
let pos = format!("{id:04}");
|
||||
super::tests::node(id, parent.map(|p| (p, pos.as_str())), ty, name, more)
|
||||
}
|
||||
|
||||
fn symbol(main: u64, overrides: Vec<Kv<'static>>) -> Kv<'static> {
|
||||
o(vec![("symbolID", g(main)), ("symbolOverrides", arr(overrides))])
|
||||
}
|
||||
|
||||
fn row(path: &[u64], more: Fields) -> Kv<'static> {
|
||||
let mut fields = vec![("guidPath", o(vec![("guids", arr(path.iter().map(|id| g(*id)).collect()))]))];
|
||||
fields.extend(more);
|
||||
o(fields)
|
||||
}
|
||||
|
||||
/// A Badge main with a label, a Card main holding a Badge instance whose `overriddenSymbolID` is
|
||||
/// the empty GUID (as in the report) and a second one the Card swaps to an Error variant, and a
|
||||
/// Card instance whose override rows write the empty GUID over both.
|
||||
fn import_cards() -> (Document, Vec<String>) {
|
||||
let message = o(vec![(
|
||||
"nodeChanges",
|
||||
arr(vec![
|
||||
node(1, None, "DOCUMENT", "Document", vec![]),
|
||||
node(2, Some(1), "CANVAS", "Page", vec![]),
|
||||
node(10, Some(2), "SYMBOL", "Property 1=Informative", vec![]),
|
||||
node(11, Some(10), "RECTANGLE", "Label", vec![]),
|
||||
node(20, Some(2), "SYMBOL", "Property 1=Error", vec![]),
|
||||
node(21, Some(20), "RECTANGLE", "Error label", vec![]),
|
||||
node(30, Some(2), "SYMBOL", "Card", vec![]),
|
||||
node(31, Some(30), "INSTANCE", "Badge", vec![("symbolData", symbol(10, vec![])), ("overriddenSymbolID", empty())]),
|
||||
node(32, Some(30), "INSTANCE", "Swapped badge", vec![("symbolData", symbol(10, vec![])), ("overriddenSymbolID", g(20))]),
|
||||
node(
|
||||
40,
|
||||
Some(2),
|
||||
"INSTANCE",
|
||||
"Card copy",
|
||||
vec![(
|
||||
"symbolData",
|
||||
symbol(30, vec![row(&[31], vec![("overriddenSymbolID", empty())]), row(&[32], vec![("overriddenSymbolID", empty())])]),
|
||||
)],
|
||||
),
|
||||
]),
|
||||
)]);
|
||||
// The export schema: the shared test schema has no `overriddenSymbolID`.
|
||||
let schema = Schema::parse(export::SCHEMA).unwrap();
|
||||
import(&canvas(&schema, &message), "Empty swap.fig").unwrap()
|
||||
}
|
||||
|
||||
fn named(doc: &Document, name: &str) -> NodeId {
|
||||
doc.nodes().find(|n| n.name == name).unwrap().id
|
||||
}
|
||||
|
||||
fn badge_in(doc: &Document, card: &str, index: usize) -> NodeId {
|
||||
doc.node(named(doc, card)).unwrap().children[index]
|
||||
}
|
||||
|
||||
fn main_of(doc: &Document, id: NodeId) -> Option<NodeId> {
|
||||
match doc.node(id)?.kind {
|
||||
NodeKind::Instance { component, .. } => Some(component),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_empty_swap_guid_keeps_the_instance_of_its_main() {
|
||||
let (doc, notes) = import_cards();
|
||||
assert!(notes.is_empty(), "no missing components: {notes:?}");
|
||||
let main = named(&doc, "Property 1=Informative");
|
||||
for card in ["Card", "Card copy"] {
|
||||
let b = doc.node(badge_in(&doc, card, 0)).unwrap();
|
||||
assert!(matches!(b.kind, NodeKind::Instance { component, .. } if component == main), "{card}: {:?}", b.kind);
|
||||
let label = b.children.first().and_then(|c| doc.node(*c)).map(|n| n.name.clone());
|
||||
assert_eq!(label.as_deref(), Some("Label"), "{card}: the main's layers");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_empty_swap_guid_on_a_row_resets_a_swap_below_it_to_the_default_main() {
|
||||
let (doc, notes) = import_cards();
|
||||
assert!(notes.is_empty(), "{notes:?}");
|
||||
// In the Card main the badge is swapped to Error; the Card instance's row writes the empty
|
||||
// GUID over that swap: back to the main its symbolData names.
|
||||
assert_eq!(main_of(&doc, badge_in(&doc, "Card", 1)), Some(named(&doc, "Property 1=Error")));
|
||||
assert_eq!(main_of(&doc, badge_in(&doc, "Card copy", 1)), Some(named(&doc, "Property 1=Informative")));
|
||||
}
|
||||
Reference in new issue
Block a user