first commit
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
using AssetStoreTools.Validator.Data;
|
||||
using AssetStoreTools.Validator.TestDefinitions;
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AssetStoreTools.Validator.UIElements
|
||||
{
|
||||
internal class AutomatedTestElement : VisualElement
|
||||
{
|
||||
private const string IconsPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Icons";
|
||||
|
||||
private readonly AutomatedTest _test;
|
||||
private TestResult.ResultStatus _lastStatus;
|
||||
|
||||
private VisualElement _expandedBox;
|
||||
private VisualElement _resultMessagesBox;
|
||||
|
||||
private Label _expanderLabel;
|
||||
private Button _foldoutBox;
|
||||
private Image _testImage;
|
||||
|
||||
private bool _expanded;
|
||||
|
||||
public AutomatedTestElement(AutomatedTest test)
|
||||
{
|
||||
_test = test;
|
||||
ConstructAutomatedTest();
|
||||
|
||||
var sceneChangeHandler = new EditorSceneManager.SceneOpenedCallback((_, __) => ResultChanged());
|
||||
EditorSceneManager.sceneOpened += sceneChangeHandler;
|
||||
AssetStoreValidator.OnWindowDestroyed += () => EditorSceneManager.sceneOpened -= sceneChangeHandler;
|
||||
}
|
||||
|
||||
public AutomatedTest GetAutomatedTest()
|
||||
{
|
||||
return _test;
|
||||
}
|
||||
|
||||
public TestResult.ResultStatus GetLastStatus()
|
||||
{
|
||||
return _lastStatus;
|
||||
}
|
||||
|
||||
public void ResultChanged()
|
||||
{
|
||||
ClearMessages();
|
||||
|
||||
_testImage.image = GetIconByStatus();
|
||||
_lastStatus = _test.Result.Result;
|
||||
|
||||
if (_test.Result.MessageCount == 0)
|
||||
return;
|
||||
|
||||
ShowMessages();
|
||||
}
|
||||
|
||||
private void ClearMessages()
|
||||
{
|
||||
_resultMessagesBox?.Clear();
|
||||
}
|
||||
|
||||
private void ShowMessages()
|
||||
{
|
||||
var result = _test.Result;
|
||||
|
||||
_resultMessagesBox?.RemoveFromHierarchy();
|
||||
|
||||
_resultMessagesBox = new VisualElement();
|
||||
_resultMessagesBox.AddToClassList("result-messages-box");
|
||||
|
||||
switch (result.Result)
|
||||
{
|
||||
case TestResult.ResultStatus.Pass:
|
||||
_resultMessagesBox.AddToClassList("result-messages-box-pass");
|
||||
break;
|
||||
case TestResult.ResultStatus.Warning:
|
||||
_resultMessagesBox.AddToClassList("result-messages-box-warning");
|
||||
break;
|
||||
case TestResult.ResultStatus.Fail:
|
||||
_resultMessagesBox.AddToClassList("result-messages-box-fail");
|
||||
break;
|
||||
}
|
||||
|
||||
_expandedBox.Add(_resultMessagesBox);
|
||||
|
||||
for (int i = 0; i < result.MessageCount; i++)
|
||||
{
|
||||
var resultText = result.GetMessage(i).GetText();
|
||||
var clickAction = result.GetMessage(i).ClickAction;
|
||||
var messageObjects = result.GetMessage(i).GetMessageObjects();
|
||||
|
||||
var resultMessage = new VisualElement {name = "ResultMessageElement"};
|
||||
resultMessage.AddToClassList("information-box");
|
||||
|
||||
var informationButton = new Button();
|
||||
informationButton.AddToClassList("result-information-button");
|
||||
|
||||
if (result.GetMessage(i).ClickAction != null)
|
||||
{
|
||||
informationButton.tooltip = clickAction.ActionTooltip;
|
||||
informationButton.clicked += clickAction.Execute;
|
||||
informationButton.SetEnabled(true);
|
||||
}
|
||||
|
||||
var informationDescription = new Label {name = "InfoDesc", text = resultText};
|
||||
informationDescription.AddToClassList("test-reason-desc");
|
||||
|
||||
informationButton.Add(informationDescription);
|
||||
resultMessage.Add(informationButton);
|
||||
|
||||
foreach (var obj in messageObjects)
|
||||
{
|
||||
if (obj == null)
|
||||
continue;
|
||||
|
||||
var objectField = new ObjectField() {objectType = obj.GetType(), value = obj};
|
||||
objectField.RegisterCallback<ChangeEvent<UnityEngine.Object>>((evt) =>
|
||||
objectField.SetValueWithoutNotify(evt.previousValue));
|
||||
resultMessage.Add(objectField);
|
||||
}
|
||||
|
||||
_resultMessagesBox.Add(resultMessage);
|
||||
|
||||
if (i == result.MessageCount - 1)
|
||||
continue;
|
||||
|
||||
var separator = new VisualElement() {name = "Separator"};
|
||||
separator.AddToClassList("message-separator");
|
||||
_resultMessagesBox.Add(separator);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowFunctions(bool? show=null)
|
||||
{
|
||||
if (show == null)
|
||||
_expanded = !_expanded;
|
||||
else
|
||||
_expanded = (bool) show;
|
||||
|
||||
if (_expanded)
|
||||
_foldoutBox.AddToClassList("foldout-box-expanded");
|
||||
else
|
||||
_foldoutBox.RemoveFromClassList("foldout-box-expanded");
|
||||
|
||||
_expanderLabel.text = !_expanded ? "►" : "▼";
|
||||
_expandedBox.style.display = _expanded ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
private void ConstructAutomatedTest()
|
||||
{
|
||||
name = "TestRow";
|
||||
AddToClassList("full-test-box");
|
||||
|
||||
_foldoutBox = new Button (() => {ShowFunctions();}) {name = _test.Title};
|
||||
_foldoutBox.AddToClassList("foldout-box");
|
||||
|
||||
// Expander and Asset Label
|
||||
VisualElement labelExpanderRow = new VisualElement { name = "labelExpanderRow" };
|
||||
labelExpanderRow.AddToClassList("expander-label-row");
|
||||
|
||||
_expanderLabel = new Label { name = "ExpanderLabel", text = "►" };
|
||||
_expanderLabel.AddToClassList("expander");
|
||||
|
||||
Label testLabel = new Label { name = "TestLabel", text = _test.Title };
|
||||
testLabel.AddToClassList("test-label");
|
||||
|
||||
labelExpanderRow.Add(_expanderLabel);
|
||||
labelExpanderRow.Add(testLabel);
|
||||
|
||||
_testImage = new Image
|
||||
{
|
||||
name = "TestImage",
|
||||
image = GetIconByStatus()
|
||||
};
|
||||
|
||||
_testImage.AddToClassList("test-result-image");
|
||||
|
||||
_foldoutBox.Add(labelExpanderRow);
|
||||
_foldoutBox.Add(_testImage);
|
||||
|
||||
var testCaseDescription = new TextField
|
||||
{
|
||||
name = "Description",
|
||||
value = _test.Description,
|
||||
isReadOnly = true,
|
||||
multiline = true,
|
||||
focusable = false,
|
||||
doubleClickSelectsWord = false,
|
||||
tripleClickSelectsLine = false
|
||||
};
|
||||
testCaseDescription.AddToClassList("test-description");
|
||||
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
testCaseDescription.focusable = true;
|
||||
testCaseDescription.selectAllOnFocus = false;
|
||||
testCaseDescription.selectAllOnMouseUp = false;
|
||||
#endif
|
||||
|
||||
_expandedBox = new VisualElement();
|
||||
_expandedBox.AddToClassList("test-expanded-box");
|
||||
|
||||
_expandedBox.Add(testCaseDescription);
|
||||
|
||||
Add(_foldoutBox);
|
||||
Add(_expandedBox);
|
||||
|
||||
ShowFunctions(false);
|
||||
ResultChanged();
|
||||
}
|
||||
|
||||
private Texture GetIconByStatus()
|
||||
{
|
||||
var iconTheme = "";
|
||||
if (!EditorGUIUtility.isProSkin)
|
||||
iconTheme = "_d";
|
||||
|
||||
switch (_test.Result.Result)
|
||||
{
|
||||
case TestResult.ResultStatus.Pass:
|
||||
return (Texture) EditorGUIUtility.Load($"{IconsPath}/success{iconTheme}.png");
|
||||
case TestResult.ResultStatus.Warning:
|
||||
return (Texture) EditorGUIUtility.Load($"{IconsPath}/warning{iconTheme}.png");
|
||||
case TestResult.ResultStatus.Fail:
|
||||
return (Texture) EditorGUIUtility.Load($"{IconsPath}/error{iconTheme}.png");
|
||||
case TestResult.ResultStatus.Undefined:
|
||||
return (Texture) EditorGUIUtility.Load($"{IconsPath}/undefined{iconTheme}.png");
|
||||
default:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8c0565120aa4395affe7e33e1482022
|
||||
timeCreated: 1653312885
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 115
|
||||
packageName: Asset Store Publishing Tools
|
||||
packageVersion: 11.4.3
|
||||
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI Elements/AutomatedTestElement.cs
|
||||
uploadId: 681981
|
||||
@@ -0,0 +1,251 @@
|
||||
using AssetStoreTools.Validator.Categories;
|
||||
using AssetStoreTools.Validator.Data;
|
||||
using AssetStoreTools.Validator.TestDefinitions;
|
||||
using AssetStoreTools.Validator.Utility;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AssetStoreTools.Validator.UIElements
|
||||
{
|
||||
internal class AutomatedTestsGroup : VisualElement
|
||||
{
|
||||
private readonly Dictionary<int, AutomatedTestElement> _testElements = new Dictionary<int, AutomatedTestElement>();
|
||||
private readonly Dictionary<TestResult.ResultStatus, AutomatedTestsGroupElement> _testGroupElements =
|
||||
new Dictionary<TestResult.ResultStatus, AutomatedTestsGroupElement>();
|
||||
|
||||
private ScrollView _allTestsScrollView;
|
||||
private ValidationInfoElement _validationInfoBox;
|
||||
private PathBoxElement _pathBox;
|
||||
private Button _validateButton;
|
||||
private ToolbarMenu _categoryMenu;
|
||||
|
||||
private static readonly TestResult.ResultStatus[] StatusOrder = {TestResult.ResultStatus.Undefined,
|
||||
TestResult.ResultStatus.Fail, TestResult.ResultStatus.Warning, TestResult.ResultStatus.Pass,
|
||||
TestResult.ResultStatus.VariableSeverityIssue}; // VariableSeverityFail should always be convered to Warning/Fail, but is defined as a failsafe
|
||||
|
||||
private PackageValidator _validator;
|
||||
private string _selectedCategory;
|
||||
|
||||
public AutomatedTestsGroup()
|
||||
{
|
||||
_validator = PackageValidator.Instance;
|
||||
ConstructInfoPart();
|
||||
ConstructAutomatedTests();
|
||||
|
||||
ValidationState.Instance.OnJsonSave -= Reinitialize;
|
||||
ValidationState.Instance.OnJsonSave += Reinitialize;
|
||||
}
|
||||
|
||||
private void Reinitialize()
|
||||
{
|
||||
this.Clear();
|
||||
|
||||
_testElements.Clear();
|
||||
_testGroupElements.Clear();
|
||||
|
||||
ConstructInfoPart();
|
||||
ConstructAutomatedTests();
|
||||
}
|
||||
|
||||
private void ConstructInfoPart()
|
||||
{
|
||||
_validationInfoBox = new ValidationInfoElement();
|
||||
_pathBox = new PathBoxElement();
|
||||
|
||||
var categorySelectionBox = new VisualElement();
|
||||
categorySelectionBox.AddToClassList("selection-box-row");
|
||||
|
||||
VisualElement labelHelpRow = new VisualElement();
|
||||
labelHelpRow.AddToClassList("label-help-row");
|
||||
|
||||
Label categoryLabel = new Label { text = "Category" };
|
||||
Image categoryLabelTooltip = new Image
|
||||
{
|
||||
tooltip = "Choose a base category of your package" +
|
||||
"\n\nThis can be found in the Publishing Portal when creating the package listing or just " +
|
||||
"selecting a planned one." +
|
||||
"\n\nNote: Different categories could have different severities of several test cases."
|
||||
};
|
||||
|
||||
labelHelpRow.Add(categoryLabel);
|
||||
labelHelpRow.Add(categoryLabelTooltip);
|
||||
|
||||
_categoryMenu = new ToolbarMenu {name = "CategoryMenu"};
|
||||
_categoryMenu.AddToClassList("category-menu");
|
||||
PopulateCategoryDropdown();
|
||||
|
||||
categorySelectionBox.Add(labelHelpRow);
|
||||
categorySelectionBox.Add(_categoryMenu);
|
||||
|
||||
_validateButton = new Button(RunAllTests) {text = "Validate"};
|
||||
_validateButton.AddToClassList("run-all-button");
|
||||
|
||||
_validationInfoBox.Add(categorySelectionBox);
|
||||
_validationInfoBox.Add(_pathBox);
|
||||
_validationInfoBox.Add(_validateButton);
|
||||
|
||||
Add(_validationInfoBox);
|
||||
}
|
||||
|
||||
private void ConstructAutomatedTests()
|
||||
{
|
||||
name = "AutomatedTests";
|
||||
|
||||
_allTestsScrollView = new ScrollView
|
||||
{
|
||||
viewDataKey = "scrollViewKey",
|
||||
};
|
||||
_allTestsScrollView.AddToClassList("tests-scroll-view");
|
||||
|
||||
var groupedTests = GroupTestsByStatus(_validator.AutomatedTests);
|
||||
|
||||
foreach (var status in StatusOrder)
|
||||
{
|
||||
var group = new AutomatedTestsGroupElement(status.ToString(), status, true);
|
||||
_testGroupElements.Add(status, group);
|
||||
_allTestsScrollView.Add(group);
|
||||
|
||||
if (!groupedTests.ContainsKey(status))
|
||||
continue;
|
||||
|
||||
foreach (var test in groupedTests[status])
|
||||
{
|
||||
var testElement = new AutomatedTestElement(test);
|
||||
|
||||
_testElements.Add(test.Id, testElement);
|
||||
group.AddTest(testElement);
|
||||
}
|
||||
|
||||
if (StatusOrder[StatusOrder.Length - 1] != status)
|
||||
group.AddSeparator();
|
||||
}
|
||||
|
||||
Add(_allTestsScrollView);
|
||||
}
|
||||
|
||||
private void PopulateCategoryDropdown()
|
||||
{
|
||||
var list = _categoryMenu.menu;
|
||||
|
||||
var validationStateData = ValidationState.Instance.ValidationStateData;
|
||||
|
||||
HashSet<string> categories = new HashSet<string>();
|
||||
var testData = ValidatorUtility.GetAutomatedTestCases();
|
||||
foreach (var test in testData)
|
||||
{
|
||||
AddCategoriesToSet(categories, test.CategoryInfo);
|
||||
}
|
||||
|
||||
foreach (var category in categories.OrderBy(x => x))
|
||||
{
|
||||
list.AppendAction(ConvertSlashToUnicodeSlash(category), _ => OnCategoryValueChange(category));
|
||||
}
|
||||
list.AppendAction("Other", _ => OnCategoryValueChange(string.Empty));
|
||||
|
||||
_selectedCategory = _validator.Category;
|
||||
if (validationStateData.SerializedKeys == null)
|
||||
_categoryMenu.text = "Select Category";
|
||||
else if (string.IsNullOrEmpty(_selectedCategory))
|
||||
_categoryMenu.text = "Other";
|
||||
else
|
||||
_categoryMenu.text = _validator.Category;
|
||||
}
|
||||
|
||||
private string ConvertSlashToUnicodeSlash(string text)
|
||||
{
|
||||
return text.Replace('/', '\u2215');
|
||||
}
|
||||
|
||||
private void AddCategoriesToSet(HashSet<string> set, ValidatorCategory category)
|
||||
{
|
||||
if (category == null)
|
||||
return;
|
||||
|
||||
foreach (var filter in category.Filter)
|
||||
set.Add(filter);
|
||||
}
|
||||
|
||||
private Dictionary<TestResult.ResultStatus, List<AutomatedTest>> GroupTestsByStatus(List<AutomatedTest> tests)
|
||||
{
|
||||
var groupedDictionary = new Dictionary<TestResult.ResultStatus, List<AutomatedTest>>();
|
||||
|
||||
foreach (var t in tests)
|
||||
{
|
||||
if (!groupedDictionary.ContainsKey(t.Result.Result))
|
||||
groupedDictionary.Add(t.Result.Result, new List<AutomatedTest>());
|
||||
|
||||
groupedDictionary[t.Result.Result].Add(t);
|
||||
}
|
||||
|
||||
return groupedDictionary;
|
||||
}
|
||||
|
||||
private async void RunAllTests()
|
||||
{
|
||||
var validationPaths = _pathBox.GetValidationPaths();
|
||||
if (validationPaths.Count == 0)
|
||||
return;
|
||||
|
||||
_validateButton.SetEnabled(false);
|
||||
|
||||
// Make sure everything is collected and validation button is disabled
|
||||
await Task.Delay(100);
|
||||
|
||||
var validationSettings = new ValidationSettings()
|
||||
{
|
||||
ValidationPaths = validationPaths,
|
||||
Category = _selectedCategory
|
||||
};
|
||||
|
||||
var validationResult = _validator.RunAllTests(validationSettings);
|
||||
|
||||
if(validationResult.Status != ValidationStatus.RanToCompletion)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Validation failed", $"Package validation failed: {validationResult.Error}", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update UI
|
||||
foreach(var test in validationResult.AutomatedTests)
|
||||
{
|
||||
var testElement = _testElements[test.Id];
|
||||
|
||||
var currentStatus = test.Result.Result;
|
||||
var lastStatus = testElement.GetLastStatus();
|
||||
|
||||
if (_testGroupElements.ContainsKey(lastStatus) && _testGroupElements.ContainsKey(currentStatus))
|
||||
{
|
||||
if (lastStatus != currentStatus)
|
||||
{
|
||||
_testGroupElements[lastStatus].RemoveTest(testElement);
|
||||
_testGroupElements[currentStatus].AddTest(testElement);
|
||||
|
||||
testElement.GetAutomatedTest().Result = test.Result;
|
||||
}
|
||||
}
|
||||
|
||||
testElement.ResultChanged();
|
||||
}
|
||||
|
||||
_validateButton.SetEnabled(true);
|
||||
}
|
||||
|
||||
private void OnCategoryValueChange(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
_categoryMenu.text = "Other";
|
||||
_selectedCategory = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_categoryMenu.text = value;
|
||||
_selectedCategory = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c2c433eb91b4ca3b7113d88ba3cfb96
|
||||
timeCreated: 1653382235
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 115
|
||||
packageName: Asset Store Publishing Tools
|
||||
packageVersion: 11.4.3
|
||||
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI Elements/AutomatedTestsGroup.cs
|
||||
uploadId: 681981
|
||||
@@ -0,0 +1,143 @@
|
||||
using AssetStoreTools.Validator.Data;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AssetStoreTools.Validator.UIElements
|
||||
{
|
||||
internal class AutomatedTestsGroupElement : VisualElement
|
||||
{
|
||||
private const string IconsPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Icons";
|
||||
|
||||
private string GroupName { get; }
|
||||
private TestResult.ResultStatus GroupStatus { get; }
|
||||
private readonly List<AutomatedTestElement> _tests;
|
||||
|
||||
private Button _groupExpanderBox;
|
||||
private Image _groupImage;
|
||||
private VisualElement _groupContent;
|
||||
|
||||
private Label _expanderLabel;
|
||||
private Label _groupLabel;
|
||||
|
||||
private bool _expanded;
|
||||
|
||||
public AutomatedTestsGroupElement(string groupName, TestResult.ResultStatus groupStatus, bool createExpanded)
|
||||
{
|
||||
GroupName = groupName;
|
||||
GroupStatus = groupStatus;
|
||||
AddToClassList("tests-group");
|
||||
|
||||
_tests = new List<AutomatedTestElement>();
|
||||
_expanded = createExpanded;
|
||||
|
||||
SetupSingleGroupElement();
|
||||
HandleExpanding();
|
||||
}
|
||||
|
||||
public void AddTest(AutomatedTestElement test)
|
||||
{
|
||||
_tests.Add(test);
|
||||
_groupContent.Add(test);
|
||||
|
||||
UpdateGroupLabel();
|
||||
ShowGroup();
|
||||
}
|
||||
|
||||
public void RemoveTest(AutomatedTestElement test)
|
||||
{
|
||||
if (_tests.Contains(test))
|
||||
_tests.Remove(test);
|
||||
|
||||
if (_groupContent.Contains(test))
|
||||
_groupContent.Remove(test);
|
||||
|
||||
ShowGroup();
|
||||
}
|
||||
|
||||
public void AddSeparator()
|
||||
{
|
||||
var groupSeparator = new VisualElement {name = "GroupSeparator"};
|
||||
groupSeparator.AddToClassList("group-separator");
|
||||
|
||||
_groupContent.Add(groupSeparator);
|
||||
}
|
||||
|
||||
private void ShowGroup()
|
||||
{
|
||||
style.display = _tests.Count > 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
private void SetupSingleGroupElement()
|
||||
{
|
||||
_groupExpanderBox = new Button();
|
||||
_groupExpanderBox.AddToClassList("group-expander-box");
|
||||
|
||||
_expanderLabel = new Label { name = "ExpanderLabel", text = "►" };
|
||||
_expanderLabel.AddToClassList("expander");
|
||||
|
||||
_groupImage = new Image
|
||||
{
|
||||
name = "TestImage",
|
||||
image = GetIconByStatus()
|
||||
};
|
||||
_groupImage.AddToClassList("group-image");
|
||||
|
||||
_groupLabel = new Label {text = $"{GroupName} ({_tests.Count})"};
|
||||
_groupLabel.AddToClassList("group-label");
|
||||
|
||||
_groupExpanderBox.Add(_expanderLabel);
|
||||
_groupExpanderBox.Add(_groupImage);
|
||||
_groupExpanderBox.Add(_groupLabel);
|
||||
|
||||
_groupContent = new VisualElement {name = "GroupContentBox"};
|
||||
_groupContent.AddToClassList("group-content-box");
|
||||
|
||||
_groupExpanderBox.clicked += () =>
|
||||
{
|
||||
_expanded = !_expanded;
|
||||
|
||||
HandleExpanding();
|
||||
};
|
||||
|
||||
Add(_groupExpanderBox);
|
||||
Add(_groupContent);
|
||||
}
|
||||
|
||||
private void HandleExpanding()
|
||||
{
|
||||
var expanded = _expanded;
|
||||
|
||||
_expanderLabel.text = !expanded ? "►" : "▼";
|
||||
var displayStyle = expanded ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
_groupContent.style.display = displayStyle;
|
||||
}
|
||||
|
||||
private void UpdateGroupLabel()
|
||||
{
|
||||
_groupLabel.text = $"{GroupName} ({_tests.Count})";
|
||||
}
|
||||
|
||||
private Texture GetIconByStatus()
|
||||
{
|
||||
var iconTheme = "";
|
||||
if (!EditorGUIUtility.isProSkin)
|
||||
iconTheme = "_d";
|
||||
|
||||
switch (GroupStatus)
|
||||
{
|
||||
case TestResult.ResultStatus.Pass:
|
||||
return (Texture) EditorGUIUtility.Load($"{IconsPath}/success{iconTheme}.png");
|
||||
case TestResult.ResultStatus.Warning:
|
||||
return (Texture) EditorGUIUtility.Load($"{IconsPath}/warning{iconTheme}.png");
|
||||
case TestResult.ResultStatus.Fail:
|
||||
return (Texture) EditorGUIUtility.Load($"{IconsPath}/error{iconTheme}.png");
|
||||
case TestResult.ResultStatus.Undefined:
|
||||
return (Texture) EditorGUIUtility.Load($"{IconsPath}/undefined{iconTheme}.png");
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e148fdea53b04663934cd1780333a046
|
||||
timeCreated: 1654257538
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 115
|
||||
packageName: Asset Store Publishing Tools
|
||||
packageVersion: 11.4.3
|
||||
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI Elements/AutomatedTestsGroupElement.cs
|
||||
uploadId: 681981
|
||||
@@ -0,0 +1,248 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AssetStoreTools.Utility;
|
||||
using AssetStoreTools.Validator.Data;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AssetStoreTools.Validator.UIElements
|
||||
{
|
||||
internal class PathBoxElement : VisualElement
|
||||
{
|
||||
private VisualElement _pathSelectionColumn;
|
||||
private List<Label> _validationPaths;
|
||||
private ScrollView _pathBoxScrollView;
|
||||
|
||||
public PathBoxElement()
|
||||
{
|
||||
ConstructPathBox();
|
||||
}
|
||||
|
||||
public List<string> GetValidationPaths()
|
||||
{
|
||||
return FilterValidationPaths();
|
||||
}
|
||||
|
||||
private List<string> FilterValidationPaths()
|
||||
{
|
||||
var filteredPaths = new List<string>();
|
||||
foreach(var textField in _validationPaths)
|
||||
{
|
||||
var path = textField.text;
|
||||
|
||||
// Exclude empty paths
|
||||
if (string.IsNullOrEmpty(path))
|
||||
continue;
|
||||
|
||||
// Exclude hidden paths
|
||||
if(path.Split('/').Any(x => x.EndsWith("~")))
|
||||
{
|
||||
Debug.LogWarning($"Path '{path}' cannot be validated as it is a hidden folder and not part of the Asset Database");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude paths that were serialized during previous validations, but no longer exist
|
||||
if(!Directory.Exists(path))
|
||||
{
|
||||
Debug.LogWarning($"Path '{path}' cannot be validated because it no longer exists");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prevent redundancy for new paths
|
||||
if (filteredPaths.Any(x => path.StartsWith(x + "/")))
|
||||
continue;
|
||||
|
||||
// Prevent redundancy for already added paths
|
||||
var redundantPaths = filteredPaths.Where(x => x.StartsWith(path + "/")).ToArray();
|
||||
foreach (var redundantPath in redundantPaths)
|
||||
filteredPaths.Remove(redundantPath);
|
||||
|
||||
filteredPaths.Add(path);
|
||||
}
|
||||
|
||||
return filteredPaths;
|
||||
}
|
||||
|
||||
private void ConstructPathBox()
|
||||
{
|
||||
AddToClassList("path-box");
|
||||
|
||||
_pathSelectionColumn = new VisualElement();
|
||||
_pathSelectionColumn.AddToClassList("selection-box-column");
|
||||
|
||||
var pathSelectionRow = new VisualElement();
|
||||
pathSelectionRow.AddToClassList("selection-box-row");
|
||||
|
||||
VisualElement labelHelpRow = new VisualElement();
|
||||
labelHelpRow.AddToClassList("label-help-row");
|
||||
labelHelpRow.style.alignSelf = Align.FlexStart;
|
||||
|
||||
Label pathLabel = new Label { text = "Validation paths" };
|
||||
Image pathLabelTooltip = new Image
|
||||
{
|
||||
tooltip = "Select the folder (or multiple folders) that your package consists of." +
|
||||
"\n\nAll files and folders of your package should be contained within " +
|
||||
"a single root folder that is named after your package " +
|
||||
"(e.g. 'Assets/[MyPackageName]' or 'Packages/[MyPackageName]')" +
|
||||
"\n\nIf your package includes special folders that cannot be nested within " +
|
||||
"the root package folder (e.g. 'WebGLTemplates'), they should be added to this list " +
|
||||
"together with the root package folder"
|
||||
};
|
||||
|
||||
labelHelpRow.Add(pathLabel);
|
||||
labelHelpRow.Add(pathLabelTooltip);
|
||||
|
||||
_validationPaths = new List<Label>();
|
||||
var serializedValidationState = ValidationState.Instance.ValidationStateData;
|
||||
|
||||
var fullPathBox = new VisualElement() { name = "ValidationPaths" };
|
||||
fullPathBox.AddToClassList("validation-paths-box");
|
||||
|
||||
_pathBoxScrollView = new ScrollView { name = "ValidationPathsScrollView" };
|
||||
_pathBoxScrollView.AddToClassList("validation-paths-scroll-view");
|
||||
|
||||
VisualElement scrollViewBottomRow = new VisualElement();
|
||||
scrollViewBottomRow.AddToClassList("validation-paths-scroll-view-bottom-row");
|
||||
|
||||
var addExtraPathsButton = new Button(Browse) { text = "Add a path" };
|
||||
addExtraPathsButton.AddToClassList("validation-path-add-button");
|
||||
scrollViewBottomRow.Add(addExtraPathsButton);
|
||||
|
||||
fullPathBox.Add(_pathBoxScrollView);
|
||||
fullPathBox.Add(scrollViewBottomRow);
|
||||
|
||||
pathSelectionRow.Add(labelHelpRow);
|
||||
pathSelectionRow.Add(fullPathBox);
|
||||
|
||||
_pathSelectionColumn.Add(pathSelectionRow);
|
||||
|
||||
foreach (var serializedPath in serializedValidationState.SerializedValidationPaths)
|
||||
AddPathToList(serializedPath);
|
||||
|
||||
Add(_pathSelectionColumn);
|
||||
}
|
||||
|
||||
private void AddPathToList(string path)
|
||||
{
|
||||
var validationPath = new VisualElement();
|
||||
validationPath.AddToClassList("validation-path-row");
|
||||
|
||||
var folderPathLabel = new Label(path);
|
||||
folderPathLabel.AddToClassList("path-input-field");
|
||||
_validationPaths.Add(folderPathLabel);
|
||||
|
||||
var removeButton = new Button(() =>
|
||||
{
|
||||
_pathBoxScrollView.Remove(validationPath);
|
||||
_validationPaths.Remove(folderPathLabel);
|
||||
});
|
||||
removeButton.text = "X";
|
||||
removeButton.AddToClassList("validation-path-remove-button");
|
||||
|
||||
validationPath.Add(folderPathLabel);
|
||||
validationPath.Add(removeButton);
|
||||
|
||||
_pathBoxScrollView.Add(validationPath);
|
||||
}
|
||||
|
||||
private void Browse()
|
||||
{
|
||||
string result = EditorUtility.OpenFolderPanel("Select a directory", "Assets", "");
|
||||
|
||||
if (result == string.Empty)
|
||||
return;
|
||||
|
||||
if (ValidateFolderPath(ref result) && !_validationPaths.Any(x => x.text == result))
|
||||
AddPathToList(result);
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
private bool ValidateFolderPath(ref string resultPath)
|
||||
{
|
||||
var folderPath = resultPath;
|
||||
var pathWithinProject = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length);
|
||||
|
||||
// Selected path is within the project
|
||||
if (folderPath.StartsWith(pathWithinProject))
|
||||
{
|
||||
var localPath = folderPath.Substring(pathWithinProject.Length);
|
||||
|
||||
if (localPath.StartsWith("Assets/") || localPath == "Assets")
|
||||
{
|
||||
resultPath = localPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsValidLocalPackage(localPath, out var adbPath))
|
||||
{
|
||||
resultPath = adbPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
DisplayMessage("Folder not found", "Selection must be within Assets folder or a local package.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool validLocalPackage = IsValidLocalPackage(folderPath, out var relativePackagePath);
|
||||
|
||||
bool isSymlinkedPath = false;
|
||||
string relativeSymlinkPath = string.Empty;
|
||||
|
||||
if (ASToolsPreferences.Instance.EnableSymlinkSupport)
|
||||
isSymlinkedPath = SymlinkUtil.FindSymlinkFolderRelative(folderPath, out relativeSymlinkPath);
|
||||
|
||||
// Selected path is not within the project, but could be a local package or symlinked folder
|
||||
if (!validLocalPackage && !isSymlinkedPath)
|
||||
{
|
||||
DisplayMessage("Folder not found", "Selection must be within Assets folder or a local package.");
|
||||
return false;
|
||||
}
|
||||
|
||||
resultPath = validLocalPackage ? relativePackagePath : relativeSymlinkPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsValidLocalPackage(string packageFolderPath, out string assetDatabasePackagePath)
|
||||
{
|
||||
assetDatabasePackagePath = string.Empty;
|
||||
|
||||
string packageManifestPath = $"{packageFolderPath}/package.json";
|
||||
|
||||
if (!File.Exists(packageManifestPath))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
var localPackages = PackageUtility.GetAllLocalPackages();
|
||||
|
||||
if (localPackages == null || localPackages.Length == 0)
|
||||
return false;
|
||||
|
||||
foreach (var package in localPackages)
|
||||
{
|
||||
var localPackagePath = package.GetConvenientPath();
|
||||
|
||||
if (localPackagePath != packageFolderPath)
|
||||
continue;
|
||||
|
||||
assetDatabasePackagePath = package.assetPath;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DisplayMessage(string title, string message)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog(title, message, "Okay", "Cancel"))
|
||||
Browse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f856d841ca50d7842b2850015d8b1f62
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 115
|
||||
packageName: Asset Store Publishing Tools
|
||||
packageVersion: 11.4.3
|
||||
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI Elements/PathBoxElement.cs
|
||||
uploadId: 681981
|
||||
@@ -0,0 +1,58 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AssetStoreTools.Validator.UIElements
|
||||
{
|
||||
internal class ValidationInfoElement : VisualElement
|
||||
{
|
||||
private const string GuidelinesUrl = "https://assetstore.unity.com/publishing/submission-guidelines#Overview";
|
||||
private const string SupportUrl = "https://support.unity.com/hc/en-us/requests/new?ticket_form_id=65905";
|
||||
|
||||
public ValidationInfoElement()
|
||||
{
|
||||
ConstructInformationElement();
|
||||
}
|
||||
|
||||
private void ConstructInformationElement()
|
||||
{
|
||||
AddToClassList("validation-info-box");
|
||||
|
||||
var validatorDescription = new Label
|
||||
{
|
||||
text = "Validate your package to ensure your content follows the chosen submission guidelines. " +
|
||||
"The validations below do not cover all of the content standards, and passing all validations does not " +
|
||||
"guarantee that your package will be accepted to the Asset Store.\n\n" +
|
||||
"The tests are not obligatory for submitting your assets, but they can help avoid instant rejection by the " +
|
||||
"automated vetting system, or clarify reasons of rejection communicated by the vetting team.\n\n" +
|
||||
"For more information about the validations, view the message by expanding the tests or contact our support team."
|
||||
};
|
||||
validatorDescription.AddToClassList("validator-description");
|
||||
|
||||
var submissionGuidelinesButton = new Button(() => OpenURL(GuidelinesUrl))
|
||||
{
|
||||
name = "GuidelinesButton",
|
||||
text = "Submission guidelines"
|
||||
};
|
||||
|
||||
submissionGuidelinesButton.AddToClassList("hyperlink-button");
|
||||
|
||||
var supportTicketButton = new Button(() => OpenURL(SupportUrl))
|
||||
{
|
||||
name = "SupportTicket",
|
||||
text = "Contact our support team"
|
||||
};
|
||||
|
||||
supportTicketButton.AddToClassList("hyperlink-button");
|
||||
|
||||
Add(validatorDescription);
|
||||
Add(submissionGuidelinesButton);
|
||||
Add(supportTicketButton);
|
||||
}
|
||||
|
||||
private void OpenURL(string url)
|
||||
{
|
||||
Application.OpenURL(url);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9866d77420d947ba852055eed2bac895
|
||||
timeCreated: 1653383883
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 115
|
||||
packageName: Asset Store Publishing Tools
|
||||
packageVersion: 11.4.3
|
||||
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/UI Elements/ValidationInfoElement.cs
|
||||
uploadId: 681981
|
||||
Reference in New Issue
Block a user