diff --git a/LiftLog.Maui/LiftLog.Maui.csproj b/LiftLog.Maui/LiftLog.Maui.csproj
index f481b4b9..f59f933c 100644
--- a/LiftLog.Maui/LiftLog.Maui.csproj
+++ b/LiftLog.Maui/LiftLog.Maui.csproj
@@ -69,6 +69,7 @@
true
+ true
false
true
diff --git a/LiftLog.Ui/Store/CurrentSession/CurrentSessionState.cs b/LiftLog.Ui/Store/CurrentSession/CurrentSessionState.cs
index a06f3178..f6a74535 100644
--- a/LiftLog.Ui/Store/CurrentSession/CurrentSessionState.cs
+++ b/LiftLog.Ui/Store/CurrentSession/CurrentSessionState.cs
@@ -8,5 +8,17 @@ public record CurrentSessionState(
Session? HistorySession,
Session? FeedSession,
Guid? LatestSetTimerNotificationId
- );
+ )
+ {
+ public static CurrentSessionState FromWorkoutSession(Session session)
+ {
+ return new CurrentSessionState(
+ IsHydrated: true,
+ WorkoutSession: session,
+ HistorySession: null,
+ FeedSession: null,
+ LatestSetTimerNotificationId: null
+ );
+ }
+ }
}
diff --git a/tests/LiftLog.Tests.App/Encryption/OsEncryptionServiceTests.cs b/tests/LiftLog.Tests.App/Encryption/OsEncryptionServiceTests.cs
index dfb08c4c..e66488b7 100644
--- a/tests/LiftLog.Tests.App/Encryption/OsEncryptionServiceTests.cs
+++ b/tests/LiftLog.Tests.App/Encryption/OsEncryptionServiceTests.cs
@@ -86,9 +86,9 @@ public static void Spec()
);
// Assert
- Assert.Equal(data1, decryptedData1);
- Assert.Equal(data2, decryptedData2);
- Assert.Equal(encryptedData1.IV, encryptedData2.IV);
+ data1.Should().Equal(decryptedData1);
+ data2.Should().Equal(decryptedData2);
+ encryptedData1.IV.Should().BeEquivalentTo(encryptedData2.IV);
});
});
@@ -113,7 +113,7 @@ public static void Spec()
encryptedData.EncryptedPayload[0] ^= 0xFF;
// Assert
- await Assert.ThrowsAsync(
+ Assert.ThrowsAsync(
async () =>
await sut.DecryptAesCbcAndVerifyRsa256PssAsync(
encryptedData,
diff --git a/tests/LiftLog.Tests.App/GlobalUsings.cs b/tests/LiftLog.Tests.App/GlobalUsings.cs
index fef46b37..3b2acbf2 100644
--- a/tests/LiftLog.Tests.App/GlobalUsings.cs
+++ b/tests/LiftLog.Tests.App/GlobalUsings.cs
@@ -1,7 +1,7 @@
global using System.Collections.Immutable;
global using FluentAssertions;
global using NSubstitute;
+global using NUnit.Framework;
global using Oatmilk;
+global using Oatmilk.Nunit;
global using static Oatmilk.TestBuilder;
-global using Oatmilk.Xunit;
-global using Xunit;
diff --git a/tests/LiftLog.Tests.App/LiftLog.Tests.App.csproj b/tests/LiftLog.Tests.App/LiftLog.Tests.App.csproj
index c05c8078..6afc30ff 100644
--- a/tests/LiftLog.Tests.App/LiftLog.Tests.App.csproj
+++ b/tests/LiftLog.Tests.App/LiftLog.Tests.App.csproj
@@ -10,15 +10,13 @@
-
+
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/tests/LiftLog.Tests.App/SessionBehaviors/SessionSuperset.cs b/tests/LiftLog.Tests.App/SessionBehaviors/SessionSuperset.cs
new file mode 100644
index 00000000..d1c3cba5
--- /dev/null
+++ b/tests/LiftLog.Tests.App/SessionBehaviors/SessionSuperset.cs
@@ -0,0 +1,191 @@
+using LiftLog.Lib.Models;
+using LiftLog.Ui.Store.CurrentSession;
+
+namespace LiftLog.Tests.App.SessionBehaviors;
+
+public class SessionSupersetTests
+{
+ [Describe("Session supersets")]
+ public void Spec()
+ {
+ Describe(
+ "When given a session with supersets",
+ () =>
+ {
+ Session session = null!;
+ CurrentSessionState GetState() => CurrentSessionState.FromWorkoutSession(session);
+ Session CycleExerciseReps(int exerciseIndex, int setIndex) =>
+ CurrentSessionReducers
+ .CycleExerciseReps(
+ GetState(),
+ new CycleExerciseRepsAction(
+ SessionTarget.WorkoutSession,
+ ExerciseIndex: exerciseIndex,
+ SetIndex: setIndex
+ )
+ )
+ .WorkoutSession!;
+
+ ExerciseBlueprint Exercise(int index, bool supersetWithNext) =>
+ Blueprints.CreateExerciseBlueprint(x =>
+ x with
+ {
+ Name = $"Ex{index}",
+ SupersetWithNext = supersetWithNext
+ }
+ );
+
+ BeforeEach(
+ () =>
+ session = Sessions.CreateSession(
+ sessionBlueprint: Blueprints.CreateSessionBlueprint() with
+ {
+ Exercises =
+ [
+ Exercise(0, supersetWithNext: false),
+ Exercise(1, supersetWithNext: true),
+ Exercise(2, supersetWithNext: false),
+ Exercise(3, supersetWithNext: true),
+ Exercise(4, supersetWithNext: true),
+ Exercise(5, supersetWithNext: false)
+ ]
+ },
+ fillFirstSet: false
+ )
+ );
+
+ It("should have the first exercise and set as the next exercise")
+ .When(() =>
+ {
+ var nextExercise = session.NextExercise;
+ nextExercise.Should().NotBeNull();
+ nextExercise.Blueprint.Name.Should().Be(session.RecordedExercises[0].Blueprint.Name);
+ });
+
+ Describe("and the last completed set was the first exercise (not a superset)")
+ .As(() =>
+ {
+ BeforeEach(() =>
+ {
+ session = CycleExerciseReps(0, 0);
+ });
+
+ It(
+ "Should have the next set be the first exercise",
+ () =>
+ {
+ var nextExercise = session.NextExercise;
+ nextExercise.Should().NotBeNull();
+ nextExercise
+ .Blueprint.Name.Should()
+ .Be(session.RecordedExercises[0].Blueprint.Name);
+ }
+ );
+ });
+
+ Describe(
+ "and the last completed set was the second exercise (a superset with the third exercise)"
+ )
+ .As(() =>
+ {
+ BeforeEach(() =>
+ {
+ session = CycleExerciseReps(1, 0);
+ });
+
+ It("Should have the next set be the third exercise")
+ .When(() =>
+ {
+ var nextExercise = session.NextExercise;
+ nextExercise.Should().NotBeNull();
+ nextExercise
+ .Blueprint.Name.Should()
+ .Be(session.RecordedExercises[2].Blueprint.Name);
+ });
+ });
+ Describe(
+ "and the last completed set was the third exercise (a superset with the previous exercise)"
+ )
+ .As(() =>
+ {
+ BeforeEach(() =>
+ {
+ session = CycleExerciseReps(2, 0);
+ });
+
+ It("Should have the next set be the second exercise")
+ .When(() =>
+ {
+ var nextExercise = session.NextExercise;
+ nextExercise.Should().NotBeNull();
+ nextExercise
+ .Blueprint.Name.Should()
+ .Be(session.RecordedExercises[1].Blueprint.Name);
+ });
+ });
+ Describe(
+ "and the last completed set was the fourth exercise (a superset with the fifth and sixth exercise)"
+ )
+ .As(() =>
+ {
+ BeforeEach(() =>
+ {
+ session = CycleExerciseReps(3, 0);
+ });
+
+ It("Should have the next set be the fifth exercise")
+ .When(() =>
+ {
+ var nextExercise = session.NextExercise;
+ nextExercise.Should().NotBeNull();
+ nextExercise
+ .Blueprint.Name.Should()
+ .Be(session.RecordedExercises[4].Blueprint.Name);
+ });
+ });
+
+ Describe(
+ "and the last completed set was the fifth exercise (a superset with the fourth and sixth exercise)"
+ )
+ .As(() =>
+ {
+ BeforeEach(() =>
+ {
+ session = CycleExerciseReps(4, 0);
+ });
+
+ It("Should have the next set be the sixth exercise")
+ .When(() =>
+ {
+ var nextExercise = session.NextExercise;
+ nextExercise.Should().NotBeNull();
+ nextExercise
+ .Blueprint.Name.Should()
+ .Be(session.RecordedExercises[5].Blueprint.Name);
+ });
+ });
+
+ Describe(
+ "and the last completed set was the sixth exercise (a superset with the fourth and fifth exercise)"
+ )
+ .As(() =>
+ {
+ BeforeEach(() =>
+ {
+ session = CycleExerciseReps(4, 0);
+ });
+
+ It("Should have the next set be the fourth exercise")
+ .When(() =>
+ {
+ var nextExercise = session.NextExercise;
+ nextExercise.Should().NotBeNull();
+ nextExercise
+ .Blueprint.Name.Should()
+ .Be(session.RecordedExercises[4].Blueprint.Name);
+ });
+ });
+ }
+ );
+ }
+}
diff --git a/tests/LiftLog.Tests.App/Sessions.cs b/tests/LiftLog.Tests.App/Sessions.cs
index 3f1505ac..6e66073f 100644
--- a/tests/LiftLog.Tests.App/Sessions.cs
+++ b/tests/LiftLog.Tests.App/Sessions.cs
@@ -6,7 +6,8 @@ public static class Sessions
{
public static Session CreateSession(
SessionBlueprint? sessionBlueprint = null,
- Func? transform = null
+ Func? transform = null,
+ bool fillFirstSet = true
)
{
sessionBlueprint ??= Blueprints.CreateSessionBlueprint();
@@ -15,7 +16,7 @@ public static Session CreateSession(
Id: Guid.NewGuid(),
Blueprint: sessionBlueprint,
RecordedExercises: sessionBlueprint
- .Exercises.Select(x => CreateRecordedExercise(x))
+ .Exercises.Select(x => CreateRecordedExercise(x, fillFirstSet: fillFirstSet))
.ToImmutableList(),
Date: DateOnly.Parse("2021-04-05"),
Bodyweight: null
@@ -25,7 +26,8 @@ public static Session CreateSession(
public static RecordedExercise CreateRecordedExercise(
ExerciseBlueprint? exerciseBlueprint = null,
- Func? transform = null
+ Func? transform = null,
+ bool fillFirstSet = true
)
{
exerciseBlueprint ??= Blueprints.CreateExerciseBlueprint();
@@ -37,9 +39,9 @@ public static RecordedExercise CreateRecordedExercise(
.Range(0, exerciseBlueprint.Sets)
.Select(
(i) =>
- i switch
+ (fillFirstSet, i) switch
{
- 0
+ (true, 0)
=> new PotentialSet(
new(
RepsCompleted: exerciseBlueprint.RepsPerSet,