diff --git a/Src/xWorks/ConfigurableDictionaryNode.cs b/Src/xWorks/ConfigurableDictionaryNode.cs index 3194528653..22b1eed7db 100644 --- a/Src/xWorks/ConfigurableDictionaryNode.cs +++ b/Src/xWorks/ConfigurableDictionaryNode.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 SIL International +// Copyright (c) 2014-2017 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -334,7 +334,8 @@ internal ConfigurableDictionaryNode DeepCloneUnderParent(ConfigurableDictionaryN public override int GetHashCode() { - return Parent == null ? DisplayLabel.GetHashCode() : DisplayLabel.GetHashCode() ^ Parent.GetHashCode(); + object hashingObject = DisplayLabel ?? FieldDescription; + return Parent == null ? hashingObject.GetHashCode() : hashingObject.GetHashCode() ^ Parent.GetHashCode(); } public override bool Equals(object other) diff --git a/Src/xWorks/DictionaryConfigurationController.cs b/Src/xWorks/DictionaryConfigurationController.cs index b0507fd293..382f1a4e78 100644 --- a/Src/xWorks/DictionaryConfigurationController.cs +++ b/Src/xWorks/DictionaryConfigurationController.cs @@ -1558,14 +1558,12 @@ private static void SetIsEnabledForSubTree(ConfigurableDictionaryNode node, bool } /// - /// Search the TreeNode tree to find a starting node based on matching the "class" - /// attributes of the generated XHTML tracing back from the XHTML element clicked. - /// If no match is found, SelectedNode is not set. Otherwise, the best match found - /// is used to set SelectedNode. + /// Search the TreeNode tree to find a starting node based on nodeId attribute - a hash of a ConfigurableDictionaryNode + /// generated into the xhtml. If nothing is found SelectedNode is not set. /// - internal void SetStartingNode(List classList) + internal void SetStartingNode(string nodeId) { - if (classList == null || classList.Count == 0) + if (string.IsNullOrEmpty(nodeId)) return; if (View != null && View.TreeControl != null && @@ -1579,22 +1577,15 @@ internal void SetStartingNode(List classList) var configNode = node.Tag as ConfigurableDictionaryNode; if (configNode == null) continue; - var cssClass = CssGenerator.GetClassAttributeForConfig(configNode); - if (classList[0].Split(' ').Contains(cssClass)) + topNode = FindConfigNode(configNode, nodeId, new List()); + if (topNode != null) { - topNode = configNode; break; } } - if (topNode == null) - return; - // We have a match, so search through the TreeNode tree to find the TreeNode tagged - // with the given configuration node. If found, set that as the SelectedNode. - classList.RemoveAt(0); - var startingConfigNode = FindConfigNode(topNode, classList); foreach (TreeNode node in View.TreeControl.Tree.Nodes) { - var startingTreeNode = FindMatchingTreeNode(node, startingConfigNode); + var startingTreeNode = FindMatchingTreeNode(node, topNode); if (startingTreeNode != null) { View.TreeControl.Tree.SelectedNode = startingTreeNode; @@ -1605,48 +1596,31 @@ internal void SetStartingNode(List classList) } /// - /// Recursively descend the configuration tree, progressively matching nodes against CSS class path. Stop - /// when we run out of both tree and classes. Classes can be skipped if not matched. Running out of tree nodes - /// before running out of classes causes one level of backtracking up the configuration tree to look for a better match. + /// Recursively descend the configuration tree depth first until a matching nodeId is found /// /// LT-17213 Now 'internal static' so DictionaryConfigurationDlg can use it. - internal static ConfigurableDictionaryNode FindConfigNode(ConfigurableDictionaryNode topNode, List classPath) + internal static ConfigurableDictionaryNode FindConfigNode(ConfigurableDictionaryNode topNode, string nodeId, List visited) { - if (classPath.Count == 0) + if (string.IsNullOrEmpty(nodeId) || $"{topNode.GetHashCode()}".Equals(nodeId)) { return topNode; // what we have already is the best we can find. } + visited.Add(topNode); - // If we can't go further down the configuration tree, but still have classes to match, back up one level - // and try matching with the remaining classes. The configuration tree doesn't always map exactly with - // the XHTML tree structure. For instance, in the XHTML, Examples contains instances of Example, each - // of which contains an instance of Translations, which contains instances of Translation. In the configuration - // tree, Examples contains Example and Translations at the same level. - if (topNode.ReferencedOrDirectChildren == null || topNode.ReferencedOrDirectChildren.Count == 0) - { - var match = FindConfigNode(topNode.Parent, classPath); - return ReferenceEquals(match, topNode.Parent) - ? topNode // this is the best we can find. - : match; // we found something better! - } - ConfigurableDictionaryNode matchingNode = null; - foreach (var node in topNode.ReferencedOrDirectChildren) + if (topNode.ReferencedOrDirectChildren != null) { - var cssClass = CssGenerator.GetClassAttributeForConfig(node); - // LT-17359 a reference node might have "senses mainentrysubsenses" - if (cssClass == classPath[0].Split(' ')[0]) + foreach (var node in topNode.ReferencedOrDirectChildren) { - matchingNode = node; - break; + if (visited.Contains(node)) + continue; + var match = FindConfigNode(node, nodeId, visited); + if (match != null) + { + return match; + } } } - // If we didn't match, skip this class in the list and try the next class, looking at the same configuration - // node. There are classes in the XHTML that aren't represented in the configuration nodes. ("sensecontent" - // and "sense" among others) - if (matchingNode == null) - matchingNode = topNode; - classPath.RemoveAt(0); - return FindConfigNode(matchingNode, classPath); + return null; } /// diff --git a/Src/xWorks/DictionaryConfigurationDlg.cs b/Src/xWorks/DictionaryConfigurationDlg.cs index 18fe85c0db..b0a8ce2603 100644 --- a/Src/xWorks/DictionaryConfigurationDlg.cs +++ b/Src/xWorks/DictionaryConfigurationDlg.cs @@ -230,28 +230,13 @@ private static ConfigurableDictionaryNode GetTopLevelNode(ConfigurableDictionary return childNode; } - private static bool DoesGeckoElementOriginateFromConfigNode(ConfigurableDictionaryNode configNode, GeckoElement element, - ConfigurableDictionaryNode topLevelNode) - { - Guid dummyGuid; - GeckoElement dummyElement; - var classListForGeckoElement = XhtmlDocView.GetClassListFromGeckoElement(element, out dummyGuid, out dummyElement); - classListForGeckoElement.RemoveAt(0); // don't need the top level class - var nodeToMatch = DictionaryConfigurationController.FindConfigNode(topLevelNode, classListForGeckoElement); - return Equals(nodeToMatch, configNode); - } - private static IEnumerable FindMatchingSpans(ConfigurableDictionaryNode selectedNode, GeckoElement parent, ConfigurableDictionaryNode topLevelNode, LcmCache cache) { var elements = new List(); - var desiredClass = CssGenerator.GetClassAttributeForConfig(selectedNode); - if (ConfiguredLcmGenerator.IsCollectionNode(selectedNode, cache)) - desiredClass = CssGenerator.GetClassAttributeForCollectionItem(selectedNode); foreach (var span in parent.GetElementsByTagName("span")) { - if (span.GetAttribute("class") != null && span.GetAttribute("class").Split(' ')[0] == desiredClass && - DoesGeckoElementOriginateFromConfigNode(selectedNode, span, topLevelNode)) + if (span.GetAttribute("nodeId") != null && span.GetAttribute("nodeId").Equals($"{selectedNode.GetHashCode()}")) { elements.Add(span); } diff --git a/Src/xWorks/LcmXhtmlGenerator.cs b/Src/xWorks/LcmXhtmlGenerator.cs index bf3ad8b81c..841f51a381 100644 --- a/Src/xWorks/LcmXhtmlGenerator.cs +++ b/Src/xWorks/LcmXhtmlGenerator.cs @@ -16,6 +16,7 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Text; using System.Web.UI.WebControls; using System.Xml; @@ -549,6 +550,10 @@ public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, C { xw.WriteStartElement("span"); xw.WriteAttributeString("class", CssGenerator.WritingSystemPrefix); + if (!settings.IsWebExport) + { + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + } var prefix = ((CoreWritingSystemDefinition)settings.Cache.WritingSystemFactory.get_EngineOrNull(wsId)).Abbreviation; xw.WriteString(prefix); xw.WriteEndElement(); @@ -568,6 +573,7 @@ public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, str { xw.WriteStartElement("audio"); xw.WriteAttributeString("id", safeAudioId); + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteStartElement("source"); xw.WriteAttributeString("src", srcAttribute); xw.WriteRaw(""); @@ -589,15 +595,15 @@ public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, str public IFragment WriteProcessedObject(ConfigurableDictionaryNode config, bool isBlock, IFragment elementContent, string className) { - return WriteProcessedContents(isBlock, elementContent, className); + return WriteProcessedContents(config, isBlock, elementContent, className); } public IFragment WriteProcessedCollection(ConfigurableDictionaryNode config, bool isBlock, IFragment elementContent, string className) { - return WriteProcessedContents(isBlock, elementContent, className); + return WriteProcessedContents(config, isBlock, elementContent, className); } - private IFragment WriteProcessedContents(bool asBlock, IFragment xmlContent, string className) + private IFragment WriteProcessedContents(ConfigurableDictionaryNode config, bool asBlock, IFragment xmlContent, string className) { if (!xmlContent.IsNullOrEmpty()) { @@ -643,6 +649,10 @@ public IFragment GenerateGroupingNode(ConfigurableDictionaryNode config, object { xw.WriteStartElement("span"); xw.WriteAttributeString("class", className); + if (!settings.IsWebExport) + { + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + } var innerBuilder = new StringBuilder(); foreach (var child in config.ReferencedOrDirectChildren) @@ -699,6 +709,7 @@ public void StartMultiRunString(IFragmentWriter writer, ConfigurableDictionaryNo { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement("span"); + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteAttributeString("lang", writingSystem); } @@ -712,6 +723,7 @@ public void StartBiDiWrapper(IFragmentWriter writer, ConfigurableDictionaryNode { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement("span"); // set direction on a nested span to preserve Context's position and direction. + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteAttributeString("dir", rightToLeft ? "rtl" : "ltr"); } @@ -725,6 +737,11 @@ public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement("span"); + // When generating an error node config is null + if (config != null) + { + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + } xw.WriteAttributeString("lang", writingSystem); } @@ -868,6 +885,7 @@ public void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode config var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement("div"); xw.WriteAttributeString("class", className); + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteAttributeString("id", "g" + entryGuid); } @@ -890,6 +908,7 @@ public void AddCollection(IFragmentWriter writer, ConfigurableDictionaryNode con var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement(isBlockProperty ? "div" : "span"); xw.WriteAttributeString("class", className); + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteRaw(content.ToString()); xw.WriteEndElement(); } @@ -924,6 +943,7 @@ public IFragment AddImage(ConfigurableDictionaryNode config, string classAttribu xw.WriteAttributeString("class", classAttribute); xw.WriteAttributeString("src", srcAttribute); xw.WriteAttributeString("id", "g" + pictureGuid); + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteEndElement(); xw.Flush(); return fragment; @@ -954,6 +974,7 @@ public IFragment GenerateSenseNumber(ConfigurableDictionaryNode config, string f xw.WriteStartElement("span"); xw.WriteAttributeString("class", "sensenumber"); xw.WriteAttributeString("lang", senseNumberWs); + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteString(formattedSenseNumber); xw.WriteEndElement(); xw.Flush(); @@ -1047,6 +1068,7 @@ public IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlo { xw.WriteStartElement(isBlock ? "div" : "span"); xw.WriteAttributeString("class", collectionItemClass); + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteRaw(content.ToString()); xw.WriteEndElement(); xw.Flush(); @@ -1063,6 +1085,7 @@ public IFragment AddProperty(ConfigurableDictionaryNode config, string className { xw.WriteStartElement(isBlockProperty ? "div" : "span"); xw.WriteAttributeString("class", className); + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteString(content); xw.WriteEndElement(); xw.Flush(); @@ -1082,10 +1105,11 @@ public IFragment AddSenseData(ConfigurableDictionaryNode config, IFragment sense // Wrap the number and sense combination in a sensecontent span so that both can be affected by DisplayEachSenseInParagraph xw.WriteStartElement("span"); xw.WriteAttributeString("class", "sensecontent"); - xw.WriteRaw(senseNumberSpan?.ToString()); + xw.WriteRaw(senseNumberSpan?.ToString() ?? string.Empty); xw.WriteStartElement(isBlock ? "div" : "span"); xw.WriteAttributeString("class", className); xw.WriteAttributeString("entryguid", "g" + ownerGuid); + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); xw.WriteRaw(senseContent.ToString()); xw.WriteEndElement(); // element name for property xw.WriteEndElement(); // diff --git a/Src/xWorks/XhtmlDocView.cs b/Src/xWorks/XhtmlDocView.cs index 8266cf3154..a1dee06191 100644 --- a/Src/xWorks/XhtmlDocView.cs +++ b/Src/xWorks/XhtmlDocView.cs @@ -315,7 +315,7 @@ internal static void HandleDomLeftClick(RecordClerk clerk, PropertyTable propert // or the entry being clicked (when the user clicks anywhere in an entry that is not currently selected) var destinationGuid = GetGuidFromEntryLink(element); if (destinationGuid == Guid.Empty) - GetClassListFromGeckoElement(element, out destinationGuid, out _); + GetElementInfoFromGeckoElement(element, out destinationGuid, out _); // If we don't have a destination GUID, the user may have clicked a video player. We can't handle that, // and if we say we did, we will prevent the user from operating the video controls. @@ -481,7 +481,7 @@ internal static void HandleDomRightClick(GeckoWebBrowser browser, DomMouseEventA { Guid topLevelGuid; GeckoElement entryElement; - var classList = GetClassListFromGeckoElement(element, out topLevelGuid, out entryElement); + var classList = GetElementInfoFromGeckoElement(element, out topLevelGuid, out entryElement); var localizedName = DictionaryConfigurationListener.GetDictionaryConfigurationType(propertyTable); var label = string.Format(xWorksStrings.ksConfigure, localizedName); s_contextMenu = new ContextMenuStrip(); @@ -505,28 +505,29 @@ internal static void HandleDomRightClick(GeckoWebBrowser browser, DomMouseEventA /// Returns the class hierarchy for a GeckoElement /// /// LT-17213 Internal for use in DictionaryConfigurationDlg - internal static List GetClassListFromGeckoElement(GeckoElement element, out Guid topLevelGuid, out GeckoElement entryElement) + internal static string GetElementInfoFromGeckoElement(GeckoElement element, out Guid topLevelGuid, out GeckoElement entryElement) { topLevelGuid = Guid.Empty; entryElement = element; - var classList = new List(); + string nearestNodeId = null; if (entryElement.TagName == "body" || entryElement.TagName == "html") - return classList; + return string.Empty; for (; entryElement != null; entryElement = entryElement.ParentElement) { + if (string.IsNullOrEmpty(nearestNodeId)) + { + nearestNodeId = entryElement.GetAttribute("nodeId"); + } var className = entryElement.GetAttribute("class"); - if (string.IsNullOrEmpty(className)) - continue; if (className == "letHead") break; - classList.Insert(0, className); if (entryElement.TagName == "div" && entryElement.ParentElement.TagName == "body") { topLevelGuid = GetGuidFromGeckoDomElement(entryElement); break; // we have the element we want; continuing to loop will get its parent instead } } - return classList; + return nearestNodeId; } /// @@ -598,7 +599,7 @@ private static void RunConfigureDialogAt(object sender, EventArgs e) var tagObjects = (object[])item.Tag; var propertyTable = tagObjects[0] as PropertyTable; var mediator = tagObjects[1] as Mediator; - var classList = tagObjects[2] as List; + var nodeId = tagObjects[2] as string; var guid = (Guid)tagObjects[3]; bool refreshNeeded; using (var dlg = new DictionaryConfigurationDlg(propertyTable)) @@ -611,7 +612,7 @@ private static void RunConfigureDialogAt(object sender, EventArgs e) else if (clerk != null) current = clerk.CurrentObject; var controller = new DictionaryConfigurationController(dlg, propertyTable, mediator, current); - controller.SetStartingNode(classList); + controller.SetStartingNode(nodeId); dlg.Text = String.Format(xWorksStrings.ConfigureTitle, DictionaryConfigurationListener.GetDictionaryConfigurationType(propertyTable)); dlg.HelpTopic = DictionaryConfigurationListener.GetConfigDialogHelpTopic(propertyTable); dlg.ShowDialog(propertyTable.GetValue("window")); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index afbc32b33d..1d60ade2f5 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -9479,14 +9479,14 @@ public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(tr } CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var settings = DefaultSettings; - const string antAbbrSpan = "ant"; - const string whSpan = "wh"; - const string ptSpan = "pt"; - const string antNameSpan = "Antonym"; - const string femmeSpan = "femme"; - var garçonSpan = TsStringUtils.Compose("garçon"); - var bêteSpan = TsStringUtils.Compose("bête"); - const string trucSpan = "truc"; + string antAbbrSpan = $"ant"; + string whSpan = $"wh"; + string ptSpan = $"pt"; + string antNameSpan = $"Antonym"; + string femmeSpan = $"femme"; + var garçonSpan = TsStringUtils.Compose($"garçon"); + var bêteSpan = TsStringUtils.Compose($"bête"); + string trucSpan = $"truc"; //SUT //Console.WriteLine(LcmXhtmlGenerator.SavePreviewHtmlWithStyles(new[] { manEntry.Hvo, familyEntry.Hvo, girlEntry.Hvo, individualEntry.Hvo }, null, // new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }, m_mediator)); // full output for diagnostics diff --git a/Src/xWorks/xWorksTests/DictionaryConfigurationControllerTests.cs b/Src/xWorks/xWorksTests/DictionaryConfigurationControllerTests.cs index a54fdaa963..e9c6e96b4f 100644 --- a/Src/xWorks/xWorksTests/DictionaryConfigurationControllerTests.cs +++ b/Src/xWorks/xWorksTests/DictionaryConfigurationControllerTests.cs @@ -1829,47 +1829,26 @@ public void SetStartingNode_SelectsCorrectNode() treeNode = dcc.View.TreeControl.Tree.SelectedNode; Assert.That(treeNode, Is.Null, "Passing a null class list should not find a TreeNode (and should not crash either)"); - dcc.SetStartingNode(new List()); + dcc.SetStartingNode(string.Empty); treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Null, "Passing an empty class list should not find a TreeNode"); + Assert.That(treeNode, Is.Null, "Passing an empty nodeId should not find a TreeNode"); - dcc.SetStartingNode(new List {"something","invalid"}); + dcc.SetStartingNode("12345"); treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Null, "Passing a totally invalid class list should not find a TreeNode"); + Assert.That(treeNode, Is.Null, "Passing a totally invalid nodeId hash should not find a tree node"); - dcc.SetStartingNode(new List{"entry","senses","sensecontent","sense","random","nonsense"}); + dcc.SetStartingNode($"{headwordNode.GetHashCode()}"); treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "Passing a partially valid class list should find a TreeNode"); - Assert.AreSame(sensesNode, treeNode.Tag, "Passing a partially valid class list should find the best node possible"); + Assert.That(treeNode, Is.Not.Null, "Passing the hash for headword should return a node."); + Assert.AreSame(headwordNode, treeNode.Tag, "The correct node should be identified by the hash"); // Starting here we need to Unset the controller's SelectedNode to keep from getting false positives ClearSelectedNode(dcc); - dcc.SetStartingNode(new List {"entry","mainheadword"}); + dcc.SetStartingNode($"{translationNode.GetHashCode()}"); treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "entry/mainheadword should find a TreeNode"); - Assert.AreSame(headwordNode, treeNode.Tag, "entry/mainheadword should find the right TreeNode"); - Assert.AreEqual(headwordNode.Label, treeNode.Text, "The TreeNode for entry/mainheadword should have the right Text"); - - ClearSelectedNode(dcc); - dcc.SetStartingNode(new List { "entry " + XhtmlDocView.CurrentSelectedEntryClass, "mainheadword" }); - treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "entry/mainheadword should find a TreeNode, even if this is the selected entry"); - Assert.AreSame(headwordNode, treeNode.Tag, "entry/mainheadword should find the right TreeNode"); - Assert.AreEqual(headwordNode.Label, treeNode.Text, "The TreeNode for entry/mainheadword should have the right Text"); - - ClearSelectedNode(dcc); - dcc.SetStartingNode(new List {"entry","senses","sensecontent","sense","definitionorgloss"}); - treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "entry//definitionorgloss should find a TreeNode"); - Assert.AreSame(defglossNode, treeNode.Tag, "entry//definitionorgloss should find the right TreeNode"); - Assert.AreEqual(defglossNode.Label, treeNode.Text, "The TreeNode for entry//definitionorgloss should have the right Text"); - - ClearSelectedNode(dcc); - dcc.SetStartingNode(new List {"entry","senses","sensecontent","sense","examples","example","translations","translation","translation"}); - treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "entry//translation should find a TreeNode"); - Assert.AreSame(translationNode, treeNode.Tag, "entry//translation should find the right TreeNode"); - Assert.AreEqual(translationNode.Label, treeNode.Text, "The TreeNode for entry//translation should have the right Text"); + Assert.That(treeNode, Is.Not.Null, "translation should find a TreeNode"); + Assert.AreSame(translationNode, treeNode.Tag, "using the translationNode hash should find the right TreeNode"); + Assert.AreEqual(translationNode.Label, treeNode.Text, "The translation treenode should have the right Text"); } } @@ -1902,19 +1881,7 @@ public void FindStartingConfigNode_FindsSharedNodes() FieldDescription = "LexEntry", CSSClassNameOverride = "entry", Children = new List { sensesNode } }; CssGeneratorTests.PopulateFieldsForTesting(DictionaryConfigurationModelTests.CreateSimpleSharingModel(entryNode, subSensesSharedItem)); - var node = DictionaryConfigurationController.FindConfigNode(entryNode, new List - { - "entry", - "senses", - "sensecontent", - "sense", - "senses mainentrysubsenses", - "sensecontent", - "sense mainentrysubsense", - "senses mainentrysubsenses", - "sensecontent", - "sensenumber" - }); + var node = DictionaryConfigurationController.FindConfigNode(entryNode, $"{subsubsensesNode.GetHashCode()}", new List()); Assert.AreSame(subsubsensesNode, node, "Sense Numbers are configured on the node itself, not its ReferencedOrDirectChildren.{0}Expected: {1}{0}But got: {2}", Environment.NewLine, DictionaryConfigurationMigrator.BuildPathStringFromNode(subsubsensesNode), DictionaryConfigurationMigrator.BuildPathStringFromNode(node)); @@ -2536,102 +2503,24 @@ public void SetStartingNode_WorksWithReferencedSubsenseNode() m_model.Parts = new List { entryNode }; m_model.SharedItems = new List { referencedConfigNode }; CssGeneratorTests.PopulateFieldsForTesting(m_model); - var subSenseGloss = subsenseClassListArray.ToList(); - subSenseGloss.Add("gloss"); - var subSenseUndefined = subsenseClassListArray.ToList(); - subSenseUndefined.Add("undefined"); using (var testView = new TestConfigurableDictionaryView()) { var dcc = new DictionaryConfigurationController { View = testView, _model = m_model }; dcc.CreateTreeOfTreeNodes(null, m_model.Parts); //Test normal case first - dcc.SetStartingNode(new List { "entry", "senses", "sensecontent", "sense", "gloss" }); + dcc.SetStartingNode($"{glossNode.GetHashCode()}"); var treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "Passing a valid class list should find a TreeNode"); - Assert.AreSame(glossNode, treeNode.Tag, "Passing a valid class list should find the node"); - - //SUT - ClearSelectedNode(dcc); - dcc.SetStartingNode(subSenseGloss); - treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "Passing a valid class list should find a TreeNode"); - Assert.AreSame(subGlossNode, treeNode.Tag, "Passing a valid class list should even find the node in a referenced node"); - - ClearSelectedNode(dcc); - dcc.SetStartingNode(subSenseUndefined); - treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "invalid field should still find a TreeNode"); - Assert.AreSame(subSensesNode, treeNode.Tag, "'undefined' field should find the closest TreeNode"); - } - } - - static readonly string[] subentryClassListArray = { "entry", "subentries mainentrysubentries", "subentry mainentrysubentry" }; - - [Test] - public void SetStartingNode_WorksWithReferencedSubentryNode() - { - var subentriesNode = new ConfigurableDictionaryNode - { - FieldDescription = "Subentries", - ReferenceItem = "MainEntrySubentries" - }; - var subGlossNode = new ConfigurableDictionaryNode - { - FieldDescription = "Gloss" - }; - var referencedConfigNode = new ConfigurableDictionaryNode - { - FieldDescription = "Subentries", - CSSClassNameOverride = "mainentrysubentries", - Children = new List { subGlossNode }, - Label = "MainEntrySubentries" - }; - - var headwordNode = new ConfigurableDictionaryNode - { - FieldDescription = "MLHeadWord", - Label = "Headword", - CSSClassNameOverride = "mainheadword" - }; - var entryNode = new ConfigurableDictionaryNode - { - FieldDescription = "LexEntry", - Label = "Main Entry", - CSSClassNameOverride = "entry", - Children = new List { headwordNode, subentriesNode }, - }; - m_model.Parts = new List { entryNode }; - m_model.SharedItems = new List { referencedConfigNode }; - CssGeneratorTests.PopulateFieldsForTesting(m_model); - var subentryGloss = subentryClassListArray.ToList(); - subentryGloss.Add("gloss"); - var subentryUndefined = subentryClassListArray.ToList(); - subentryUndefined.Add("undefined"); - var subentriesClassList = subentryClassListArray.ToList(); - subentriesClassList.RemoveAt(subentriesClassList.Count - 1); - using (var testView = new TestConfigurableDictionaryView()) - { - var dcc = new DictionaryConfigurationController { View = testView, _model = m_model }; - dcc.CreateTreeOfTreeNodes(null, m_model.Parts); + Assert.That(treeNode, Is.Not.Null); + Assert.AreSame(glossNode, treeNode.Tag, "Passing the normal gloss hash should get the gloss node"); //SUT - dcc.SetStartingNode(subentryGloss); - var treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "Passing a valid class list should find a TreeNode"); - Assert.AreSame(subGlossNode, treeNode.Tag, "Passing a valid class list should even find the node in a referenced node"); - - ClearSelectedNode(dcc); - dcc.SetStartingNode(subentryUndefined); - treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "invalid field should still find a TreeNode"); - Assert.AreSame(subentriesNode, treeNode.Tag, "'undefined' field should find the closest TreeNode"); - ClearSelectedNode(dcc); - dcc.SetStartingNode(subentriesClassList); + dcc.SetStartingNode($"{subGlossNode.GetHashCode()}"); treeNode = dcc.View.TreeControl.Tree.SelectedNode; - Assert.That(treeNode, Is.Not.Null, "should find main Subentries node"); - Assert.AreSame(subentriesNode, treeNode.Tag, "Passing a valid class list should find it"); + Assert.That(treeNode, Is.Not.Null); + Assert.AreSame(subGlossNode, treeNode.Tag, + "Passing the hash for the gloss on the subentry should get the subentry gloss node"); } } diff --git a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs index 6241df3c96..48875bfcac 100644 --- a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs @@ -1105,7 +1105,9 @@ public void SavePublishedJsonWithStyles_DisplayXhtmlPopulated() m_propertyTable, "test.json", null, out int[] _); var expectedResults = @"{""xhtmlTemplate"":""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""homographnumber"":""0"",""citationform"":[{""lang"":""fr"",""value"":""Citation""}], - ""displayXhtml"":""
0Citation
""}"; + ""displayXhtml"":""
0Citation
""}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(results[0][0].ToString(Formatting.None), expected); }