Add whole-release embeds to FramePlayer and un-gate the release embed share affordance
The queue gains an armed-but-idle state (Arm/Start) so a release embed stages track 0 prerender-safe, then queues the full release on first play and auto-advances.
This commit is contained in:
@@ -144,6 +144,127 @@ public class QueueServiceTests
|
||||
});
|
||||
}
|
||||
|
||||
// --- Arm: prerender-safe load without streaming (release embed) ---
|
||||
|
||||
[Test]
|
||||
public void Arm_LoadsTracksAtIndexZero_WithoutStreaming()
|
||||
{
|
||||
var tracks = Tracks(3);
|
||||
|
||||
_queue.Arm(tracks);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(_queue.Items.Select(t => t.EntryKey),
|
||||
Is.EqualTo(new[] { "track-1", "track-2", "track-3" }));
|
||||
Assert.That(_queue.CurrentIndex, Is.EqualTo(0));
|
||||
Assert.That(_queue.IsArmed, Is.True);
|
||||
// Arming is interop-free state only: nothing must have been streamed yet.
|
||||
Assert.That(_player.SelectedTracks, Is.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Arm_WithSingleTrackRelease_ArmsAOneItemQueueWithoutError()
|
||||
{
|
||||
_queue.Arm(Tracks(1));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(_queue.Items, Has.Count.EqualTo(1));
|
||||
Assert.That(_queue.CurrentIndex, Is.EqualTo(0));
|
||||
Assert.That(_queue.IsArmed, Is.True);
|
||||
Assert.That(_queue.HasNext, Is.False);
|
||||
Assert.That(_player.SelectedTracks, Is.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Arm_WithEmptyTracks_IsNoOpAndLeavesQueueDisarmed()
|
||||
{
|
||||
_queue.Arm(Enumerable.Empty<TrackDto>());
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(_queue.Items, Is.Empty);
|
||||
Assert.That(_queue.CurrentIndex, Is.EqualTo(-1));
|
||||
Assert.That(_queue.IsArmed, Is.False);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Start_OnArmedQueue_StreamsTrackZeroAndDisarms()
|
||||
{
|
||||
// Models the embed's first-gesture path: FramePlayer arms the queue (no stream), then the
|
||||
// play click routes through Start().
|
||||
_queue.Arm(Tracks(3));
|
||||
|
||||
await _queue.Start();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(_queue.CurrentIndex, Is.EqualTo(0));
|
||||
Assert.That(_queue.IsArmed, Is.False);
|
||||
Assert.That(_player.SelectedTracks.Single().EntryKey, Is.EqualTo("track-1"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Start_OnUnarmedQueue_IsNoOp()
|
||||
{
|
||||
// A non-embed flow (PlayRelease already streaming) must not be disturbed by a stray Start.
|
||||
await _queue.PlayRelease(Tracks(3));
|
||||
var streamedBefore = _player.SelectedTracks.Count;
|
||||
|
||||
await _queue.Start();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(_queue.IsArmed, Is.False);
|
||||
Assert.That(_player.SelectedTracks, Has.Count.EqualTo(streamedBefore),
|
||||
"Start on an unarmed queue must not re-stream");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ArmedQueue_StartedThenAdvancesThroughWholeReleaseOnTrackEnded()
|
||||
{
|
||||
_queue.Arm(Tracks(3));
|
||||
await _queue.Start();
|
||||
|
||||
_player.RaiseTrackEnded(); // → track-2
|
||||
_player.RaiseTrackEnded(); // → track-3
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(_queue.CurrentIndex, Is.EqualTo(2));
|
||||
Assert.That(_player.SelectedTracks.Select(t => t.EntryKey),
|
||||
Is.EqualTo(new[] { "track-1", "track-2", "track-3" }));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Arm_RaisesQueueChanged()
|
||||
{
|
||||
var raised = false;
|
||||
_queue.QueueChanged += () => raised = true;
|
||||
|
||||
_queue.Arm(Tracks(2));
|
||||
|
||||
Assert.That(raised, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Clear_DisarmsAnArmedQueue()
|
||||
{
|
||||
_queue.Arm(Tracks(2));
|
||||
|
||||
_queue.Clear();
|
||||
|
||||
Assert.That(_queue.IsArmed, Is.False);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
// --- Next / Previous mechanics and bounds ---
|
||||
|
||||
[Test]
|
||||
|
||||
Reference in New Issue
Block a user