Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 203 additions & 131 deletions .claude-plugin/skills/jaction/SKILL.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Cysharp.Threading.Tasks;
using TMPro;
Expand Down Expand Up @@ -124,6 +125,64 @@ private static GameObject Prefab
internal static bool SimulateNoPrefab;
#endif

#if UNITY_INCLUDE_TESTS
/// <summary>
/// Test hook: Gets the pool state for verification in tests.
/// Returns (activeCount, pooledCount).
/// </summary>
internal static (int activeCount, int pooledCount) TestGetPoolState()
{
return (ActiveMessageBoxes.Count, PooledMessageBoxes.Count);
}

/// <summary>
/// Test hook: Simulates clicking a button on the most recently shown message box.
/// </summary>
/// <param name="clickOk">If true, simulates clicking OK; otherwise simulates clicking Cancel.</param>
/// <returns>True if a message box was found and the click was simulated.</returns>
internal static bool TestSimulateButtonClick(bool clickOk)
{
if (ActiveMessageBoxes.Count == 0) return false;

// Get the first message box (any will do for testing)
var target = ActiveMessageBoxes.First();
target.HandleEvent(clickOk);
return true;
}

/// <summary>
/// Test hook: Gets the button visibility state of the most recently shown message box.
/// </summary>
/// <returns>Tuple of (okButtonVisible, noButtonVisible), or null if no active boxes.</returns>
internal static (bool okVisible, bool noVisible)? TestGetButtonVisibility()
{
if (ActiveMessageBoxes.Count == 0) return null;

var target = ActiveMessageBoxes.First();
if (target._buttonOk == null || target._buttonNo == null)
return null;

return (target._buttonOk.gameObject.activeSelf, target._buttonNo.gameObject.activeSelf);
}

/// <summary>
/// Test hook: Gets the text content of the most recently shown message box.
/// </summary>
/// <returns>Tuple of (title, content, okText, noText), or null if no active boxes.</returns>
internal static (string title, string content, string okText, string noText)? TestGetContent()
{
if (ActiveMessageBoxes.Count == 0) return null;

var target = ActiveMessageBoxes.First();
return (
target._title?.text,
target._content?.text,
target._textOk?.text,
target._textNo?.text
);
}
#endif

private TextMeshProUGUI _content;
private TextMeshProUGUI _textNo;
private TextMeshProUGUI _textOk;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,5 +274,259 @@ public IEnumerator Show_MultipleCalls_AllReturnFalse_WhenNoPrefab() => UniTask.T
});

#endregion

#region Pool State Tests (using test hooks)

[UnityTest]
public IEnumerator Show_IncrementsActiveCount_WhenUsingTestHandler() => UniTask.ToCoroutine(async () =>
{
// Note: When TestHandler is set, the actual UI is bypassed,
// so ActiveCount won't increase. This test verifies the expected behavior.
MessageBox.TestHandler = (_, _, _, _) => UniTask.FromResult(true);

var (initialActive, _) = MessageBox.TestGetPoolState();
await MessageBox.Show("Test", "Content");
var (finalActive, _) = MessageBox.TestGetPoolState();

// With TestHandler, no actual MessageBox is created
Assert.AreEqual(initialActive, finalActive);
});

[Test]
public void TestGetPoolState_ReturnsCorrectInitialState()
{
var (activeCount, pooledCount) = MessageBox.TestGetPoolState();

Assert.AreEqual(0, activeCount);
Assert.AreEqual(0, pooledCount);
}

[Test]
public void TestGetPoolState_AfterDispose_ReturnsZero()
{
MessageBox.Dispose();

var (activeCount, pooledCount) = MessageBox.TestGetPoolState();

Assert.AreEqual(0, activeCount);
Assert.AreEqual(0, pooledCount);
}

[Test]
public void TestSimulateButtonClick_ReturnsFalse_WhenNoActiveBoxes()
{
bool result = MessageBox.TestSimulateButtonClick(true);

Assert.IsFalse(result);
}

[Test]
public void TestGetButtonVisibility_ReturnsNull_WhenNoActiveBoxes()
{
var result = MessageBox.TestGetButtonVisibility();

Assert.IsNull(result);
}

[Test]
public void TestGetContent_ReturnsNull_WhenNoActiveBoxes()
{
var result = MessageBox.TestGetContent();

Assert.IsNull(result);
}

#endregion

#region Button Visibility Tests

[UnityTest]
public IEnumerator Show_EmptyOkText_PassesToHandler() => UniTask.ToCoroutine(async () =>
{
string receivedOk = "not-empty";

MessageBox.TestHandler = (_, _, ok, _) =>
{
receivedOk = ok;
return UniTask.FromResult(true);
};

await MessageBox.Show("Title", "Content", "", "Cancel");

// Empty string is passed through
Assert.AreEqual("", receivedOk);
});

[UnityTest]
public IEnumerator Show_EmptyNoText_PassesToHandler() => UniTask.ToCoroutine(async () =>
{
string receivedNo = "not-empty";

MessageBox.TestHandler = (_, _, _, no) =>
{
receivedNo = no;
return UniTask.FromResult(true);
};

await MessageBox.Show("Title", "Content", "OK", "");

// Empty string is passed through
Assert.AreEqual("", receivedNo);
});

[UnityTest]
public IEnumerator Show_NullOkText_PassesToHandler() => UniTask.ToCoroutine(async () =>
{
string receivedOk = "not-null";

MessageBox.TestHandler = (_, _, ok, _) =>
{
receivedOk = ok;
return UniTask.FromResult(true);
};

await MessageBox.Show("Title", "Content", null, "Cancel");

// Null is passed through
Assert.IsNull(receivedOk);
});

[UnityTest]
public IEnumerator Show_NullNoText_PassesToHandler() => UniTask.ToCoroutine(async () =>
{
string receivedNo = "not-null";

MessageBox.TestHandler = (_, _, _, no) =>
{
receivedNo = no;
return UniTask.FromResult(true);
};

await MessageBox.Show("Title", "Content", "OK", null);

// Null is passed through
Assert.IsNull(receivedNo);
});

[UnityTest]
public IEnumerator Show_BothButtonsNullOrEmpty_DefaultsToOkInHandler() => UniTask.ToCoroutine(async () =>
{
// Note: The safety check for both buttons being empty happens AFTER
// TestHandler is checked, so TestHandler receives the original null values
string receivedOk = "not-null";
string receivedNo = "not-null";

MessageBox.TestHandler = (_, _, ok, no) =>
{
receivedOk = ok;
receivedNo = no;
return UniTask.FromResult(true);
};

await MessageBox.Show("Title", "Content", null, null);

// TestHandler receives original null values
Assert.IsNull(receivedOk);
Assert.IsNull(receivedNo);
});

#endregion

#region Null Content Handling Tests

[UnityTest]
public IEnumerator Show_NullTitle_HandledGracefully() => UniTask.ToCoroutine(async () =>
{
string receivedTitle = "not-null";

MessageBox.TestHandler = (title, _, _, _) =>
{
receivedTitle = title;
return UniTask.FromResult(true);
};

bool result = await MessageBox.Show(null, "Content");

Assert.IsNull(receivedTitle);
Assert.IsTrue(result);
});

[UnityTest]
public IEnumerator Show_NullContent_HandledGracefully() => UniTask.ToCoroutine(async () =>
{
string receivedContent = "not-null";

MessageBox.TestHandler = (_, content, _, _) =>
{
receivedContent = content;
return UniTask.FromResult(true);
};

bool result = await MessageBox.Show("Title", null);

Assert.IsNull(receivedContent);
Assert.IsTrue(result);
});

[UnityTest]
public IEnumerator Show_EmptyStrings_HandledGracefully() => UniTask.ToCoroutine(async () =>
{
string receivedTitle = null;
string receivedContent = null;

MessageBox.TestHandler = (title, content, _, _) =>
{
receivedTitle = title;
receivedContent = content;
return UniTask.FromResult(true);
};

bool result = await MessageBox.Show("", "");

Assert.AreEqual("", receivedTitle);
Assert.AreEqual("", receivedContent);
Assert.IsTrue(result);
});

#endregion

#region Concurrent Operations Tests

[UnityTest]
public IEnumerator Show_MultipleConcurrent_AllComplete() => UniTask.ToCoroutine(async () =>
{
int completionCount = 0;

MessageBox.TestHandler = (_, _, _, _) =>
{
completionCount++;
return UniTask.FromResult(true);
};

// Show multiple message boxes concurrently
var task1 = MessageBox.Show("Test1", "Content1");
var task2 = MessageBox.Show("Test2", "Content2");
var task3 = MessageBox.Show("Test3", "Content3");

await UniTask.WhenAll(task1, task2, task3);

Assert.AreEqual(3, completionCount);
});

[UnityTest]
public IEnumerator CloseAll_AfterMultipleShows_ClearsAll() => UniTask.ToCoroutine(async () =>
{
MessageBox.TestHandler = (_, _, _, _) => UniTask.FromResult(true);

await MessageBox.Show("Test1", "Content1");
await MessageBox.Show("Test2", "Content2");

MessageBox.CloseAll();

var (activeCount, _) = MessageBox.TestGetPoolState();
Assert.AreEqual(0, activeCount);
});

#endregion
}
}
Loading