using System.Collections;
using System.Linq;
using NUnit.Framework;
using NightclubArcadia.Cinematics;
using Unity.Cinemachine;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
using Yarn.Unity;
namespace NightclubArcadia.PlayMode.Tests
{
///
/// Proves the scene split actually comes up.
///
/// Splitting Systems out of the level nulled seven serialized references that
/// Unity cannot carry across a scene boundary — the two NPCs' dialogue runner, UI
/// and player fields, and the reveal camera's tracking target. Each is supposed to
/// be resolved at runtime instead. An EditMode test cannot see any of that, because
/// none of it has run. This can.
///
/// If this suite fails, the game does not start. Treat it that way.
///
public class BootstrapTests
{
const string Bootstrap = "Bootstrap";
const string Systems = "Systems";
const string Level = "SC101_ConferenceHall";
[UnitySetUp]
public IEnumerator LoadFromBootstrap()
{
yield return SceneManager.LoadSceneAsync(Bootstrap, LoadSceneMode.Single);
// GameBootstrap loads the other two additively over a few frames.
var deadline = Time.realtimeSinceStartup + 20f;
while (Time.realtimeSinceStartup < deadline)
{
if (SceneManager.GetSceneByName(Systems).isLoaded &&
SceneManager.GetSceneByName(Level).isLoaded)
{
break;
}
yield return null;
}
// let Awake/Start run on everything that just loaded
for (var i = 0; i < 5; i++)
{
yield return null;
}
}
[Test]
public void Bootstrap_LoadsSystemsAndLevel()
{
Assert.IsTrue(SceneManager.GetSceneByName(Systems).isLoaded, "Systems scene did not load");
Assert.IsTrue(SceneManager.GetSceneByName(Level).isLoaded, "Level scene did not load");
Assert.AreEqual(Level, SceneManager.GetActiveScene().name,
"The level should be the active scene, so new objects and lighting land there");
}
[Test]
public void ThePlayerExists_AndIsTagged()
{
var player = GameObject.FindGameObjectWithTag("Player");
Assert.IsNotNull(player, "No object tagged Player — the reveal camera and the NPCs both need it");
}
[Test]
public void TheDialogueRunnerExists()
{
Assert.IsNotNull(Object.FindFirstObjectByType(),
"No DialogueRunner in any loaded scene");
}
///
/// Regression: "Can't start dialogue: no Yarn Project has been configured."
///
/// Asserting the runner merely exists is not enough — it auto-starts on Start(),
/// and a runner with no project throws that at load with nothing else wrong.
/// The project is a serialized asset reference supplied by a prefab override, so
/// it breaks silently if the override is ever dropped.
///
[Test]
public void TheDialogueRunner_HasItsYarnProject()
{
var runners = Object.FindObjectsByType(
FindObjectsInactive.Include, FindObjectsSortMode.None);
Assert.AreEqual(1, runners.Length,
"Expected exactly one DialogueRunner across the loaded scenes, found " +
string.Join(", ", runners.Select(r => $"'{r.name}' in {r.gameObject.scene.name}")));
var runner = runners[0];
Assert.IsNotNull(runner.YarnProject,
$"'{runner.name}' in scene '{runner.gameObject.scene.name}' has no Yarn Project. " +
"It auto-starts, so this is the 'no Yarn Project has been configured' error.");
Assert.IsNotEmpty(runner.YarnProject.NodeNames, "The Yarn Project compiled no nodes");
}
[Test]
public void EveryNpc_ResolvedItsDialogueRunner()
{
var npcs = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None);
Assert.IsNotEmpty(npcs, "Expected NPCs in the level scene");
foreach (var npc in npcs)
{
Assert.IsNotNull(npc.dialogueRunner,
$"'{npc.name}' did not resolve a DialogueRunner. It lives in the Systems scene, " +
"so the serialized reference is gone and SceneServices.Resolve has to find it.");
}
}
[Test]
public void RevealCameras_BoundToThePlayer()
{
var binders = Object.FindObjectsByType(
FindObjectsInactive.Include, FindObjectsSortMode.None);
Assert.IsNotEmpty(binders, "Expected at least one runtime-bound camera");
foreach (var binder in binders)
{
Assert.IsNotNull(binder.GetComponent().Follow,
$"'{binder.name}' never bound its Follow target to the player");
}
}
}
}