Danro-Jump/Assets/Tests/EditMode/Editor/MainMenuControllerServiceActionTests.cs
Danil Iarosh d39d160d92 Implement physical prize dispensing, service stats, and QR screen timeout
- Добавлен асинхронный процесс аппаратной выдачи физических призов (хоппер и витрина) в GameplayHudController.
- Реализована блокировка выдачи по 10-секундному таймауту датчика хоппера с гарантированной отправкой команды отключения двигателя '300'.
- Реализован автоматический таймаут неактивности на экране QR-кода с последующим переходом в главное меню.
- Расширен сервис статистики PrizeSessionStateService новыми долгосрочными счетчиками (сыгранные игры, очки, монеты, сбои связи COM, физ. выдачи и ошибки).
- Обновлен построитель сводки статистики в меню настроек для отображения новых счетчиков и списка последних 5 сессий.
- Написаны EditMode тесты для проверки инкремента счетчиков и запуска выдачи призов.
2026-06-23 10:36:13 +03:00

222 lines
9.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Reflection;
using DanroJump.Prizes.Qr;
using DanroJump.Settings;
using DanroJump.UI.MainMenu;
using DanroJump.UI.Modals;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.UIElements;
namespace DanroJump.Tests.EditMode
{
public sealed class MainMenuControllerServiceActionTests
{
private GameObject gameObject;
[TearDown]
public void TearDown()
{
if (gameObject != null)
{
UnityEngine.Object.DestroyImmediate(gameObject);
}
}
[Test]
public void QrSettingsAction_OpensQrTierSettingsModal()
{
var controller = CreateController();
var settings = new StubSettingsService();
var qrSettingsRoot = CreateQrSettingsRoot();
var qrSettingsModal = new QrCodeSettingsModalWindow(qrSettingsRoot, settings);
TestObjectFactory.SetPrivateField(controller, "serviceSettingsService", settings);
TestObjectFactory.SetPrivateField(controller, "qrCodeSettingsModal", qrSettingsModal);
TestObjectFactory.SetPrivateField(controller, "modalStack", new ModalStackController(new VisualElement(), null));
InvokeServiceAction(
controller,
ServiceSettingsActionResult.Success(
"Настройки QR",
"Открыты настройки QR.",
ServiceSettingsKeys.QrPrizeTextSettings));
Assert.That(qrSettingsModal.IsOpen, Is.True);
Assert.That(qrSettingsRoot.Q<Label>("QrSettingsTierValue").text, Is.EqualTo("Бронзовый"));
Assert.That(qrSettingsRoot.Q<Label>("QrSettingsPrizeTextValue").text, Is.EqualTo("Бронза"));
qrSettingsModal.Dispose();
}
[Test]
public void OpenQrPrizeTextInput_SavesSelectedTierPrizeTextWithoutTechnicalTemplate()
{
var controller = CreateController();
var settings = new StubSettingsService();
var qrSettingsModal = new QrCodeSettingsModalWindow(CreateQrSettingsRoot(), settings);
var textInputRoot = CreateTextInputRoot();
var textInputModal = new ServiceTextInputModalWindow(textInputRoot);
TestObjectFactory.SetPrivateField(controller, "serviceSettingsService", settings);
TestObjectFactory.SetPrivateField(controller, "serviceTextInputModal", textInputModal);
TestObjectFactory.SetPrivateField(controller, "qrCodeSettingsModal", qrSettingsModal);
TestObjectFactory.SetPrivateField(controller, "modalStack", new ModalStackController(new VisualElement(), null));
InvokeOpenQrPrizeTextInput(controller, QrPrizeTier.Silver, ServiceSettingsKeys.QrPrizeTextSilver);
Assert.That(textInputModal.IsOpen, Is.True);
Assert.That(textInputRoot.Q<Label>("ServiceTextInputSetting").text, Is.EqualTo("Серебряный QR"));
Assert.That(textInputRoot.Q<Label>("ServiceTextInputPreview").text, Is.EqualTo("Серебро"));
Assert.That(textInputRoot.Q<Label>("ServiceTextInputPreview").text, Does.Not.Contain("{score}"));
Assert.That(textInputModal.Confirm(), Is.True);
Assert.That(settings.LastSetStringKey, Is.EqualTo(ServiceSettingsKeys.QrPrizeTextSilver));
Assert.That(settings.LastSetStringValue, Is.EqualTo("Серебро"));
Assert.That(settings.SaveCalls, Is.EqualTo(1));
qrSettingsModal.Dispose();
textInputModal.Dispose();
}
private MainMenuController CreateController()
{
gameObject = new GameObject("MainMenuControllerServiceActionTest");
return gameObject.AddComponent<MainMenuController>();
}
private static void InvokeServiceAction(
MainMenuController controller,
ServiceSettingsActionResult result)
{
var method = typeof(MainMenuController).GetMethod(
"HandleServiceActionActivated",
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.That(method, Is.Not.Null);
method.Invoke(controller, new object[] { null, result });
}
private static void InvokeOpenQrPrizeTextInput(
MainMenuController controller,
QrPrizeTier tier,
string settingKey)
{
var method = typeof(MainMenuController).GetMethod(
"OpenQrPrizeTextInput",
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.That(method, Is.Not.Null);
method.Invoke(controller, new object[] { tier, settingKey });
}
private static VisualElement CreateQrSettingsRoot()
{
var root = new VisualElement { name = "QrSettingsModal" };
root.Add(new Label { name = "QrSettingsTierValue" });
root.Add(new Label { name = "QrSettingsPrizeTextValue" });
root.Add(new Label { name = "QrSettingsStatus" });
root.Add(new Button { name = "QrSettingsTierPreviousButton" });
root.Add(new Button { name = "QrSettingsTierNextButton" });
root.Add(new Button { name = "QrSettingsEditTextButton" });
root.Add(new Button { name = "QrSettingsCloseButton" });
return root;
}
private static VisualElement CreateTextInputRoot()
{
var root = new VisualElement { name = "ServiceTextInputModal" };
root.Add(new Label { name = "ServiceTextInputTitle" });
root.Add(new Label { name = "ServiceTextInputSetting" });
root.Add(new Label { name = "ServiceTextInputPreview" });
root.Add(new VisualElement { name = "ServiceTextInputSlots" });
root.Add(new Label { name = "ServiceTextInputSymbol" });
root.Add(new Label { name = "ServiceTextInputStatus" });
root.Add(new Button { name = "ServiceTextInputConfirmButton" });
root.Add(new Button { name = "ServiceTextInputDeleteButton" });
root.Add(new Button { name = "ServiceTextInputCancelButton" });
return root;
}
private sealed class StubSettingsService : IServiceSettingsService
{
private readonly Dictionary<string, ServiceSettingDefinition> definitions = new();
private readonly Dictionary<string, string> values = new();
public StubSettingsService()
{
AddStringDefinition(ServiceSettingsKeys.QrPrizeTextBronze, "Бронзовый QR", "Бронза");
AddStringDefinition(ServiceSettingsKeys.QrPrizeTextSilver, "Серебряный QR", "Серебро");
AddStringDefinition(ServiceSettingsKeys.QrPrizeTextGold, "Золотой QR", "Золото");
}
public string LastSetStringKey { get; private set; }
public string LastSetStringValue { get; private set; }
public int SaveCalls { get; private set; }
public ServiceSettingsDatabase Database => null;
public event Action SettingsLoaded { add { } remove { } }
public event Action SettingsSaved { add { } remove { } }
public event Action<string, ServiceSettingValue> SettingChanged { add { } remove { } }
public bool TryGetDefinition(string key, out ServiceSettingDefinition definition)
{
return definitions.TryGetValue(key, out definition);
}
public IReadOnlyDictionary<string, ServiceSettingValue> Snapshot()
{
return new Dictionary<string, ServiceSettingValue>();
}
public ServiceSettingValue Get(string key)
{
return ServiceSettingValue.String(GetString(key));
}
public bool GetBool(string key) => false;
public int GetInt(string key) => 0;
public float GetFloat(string key) => 0f;
public string GetString(string key)
{
return values.TryGetValue(key, out var value) ? value : string.Empty;
}
public int GetOptionIndex(string key) => 0;
public void Load() { }
public void Save() => SaveCalls++;
public void ReloadExternalChanges() { }
public void ApplyAll() { }
public void ResetAllToDefaults() { }
public void ResetGroupToDefaults(string groupName) { }
public bool Set(string key, ServiceSettingValue value) => true;
public bool SetExternal(string key, ServiceSettingValue value) => true;
public bool SetBool(string key, bool value) => true;
public bool SetInt(string key, int value) => true;
public bool SetFloat(string key, float value) => true;
public bool SetString(string key, string value)
{
LastSetStringKey = key;
LastSetStringValue = value;
values[key] = value;
return true;
}
public bool SetOptionIndex(string key, int optionIndex) => true;
private void AddStringDefinition(string key, string displayName, string value)
{
values[key] = value;
var definition = TestObjectFactory.CreateSettingDefinition(
key,
ServiceSettingValueType.String,
ServiceSettingValue.String(value));
TestObjectFactory.SetPrivateField(definition, "displayName", displayName);
definitions[key] = definition;
}
}
}
}