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
28 changes: 28 additions & 0 deletions Docs/pages/docs/expectations/11-date-time-only.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,34 @@ TimeOnly subjectB = TimeOnly.FromDateTime(DateTime.Now);
await Expect.That(subjectB).IsBefore(TimeOnly.FromDateTime(DateTime.Now)).Within(TimeSpan.FromSeconds(1));
```

## Between

You can verify that the `DateOnly` or `TimeOnly` is between two values:

```csharp
DateOnly subjectA = DateOnly.FromDateTime(DateTime.Now);

await Expect.That(subjectA).IsBetween(new DateOnly(2024, 1, 1)).And(new DateOnly(2123, 12, 31));

TimeOnly subjectB = TimeOnly.FromDateTime(DateTime.Now);

await Expect.That(subjectB)
.IsBetween(TimeOnly.FromDateTime(DateTime.Now).Add(-2.Seconds()))
.And(TimeOnly.FromDateTime(DateTime.Now).Add(2.Seconds()));
```

You can also specify a tolerance:

```csharp
TimeOnly subject = TimeOnly.FromDateTime(DateTime.Now);

await Expect.That(subject)
.IsBetween(TimeOnly.FromDateTime(DateTime.Now))
.And(TimeOnly.FromDateTime(DateTime.Now))
.Within(2.Seconds())
.Because("it should have taken less than two seconds");
```

## Properties

You can verify, the properties of the `DateTime`:
Expand Down
1 change: 0 additions & 1 deletion Docs/pages/docs/expectations/12-datetime-offset.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ DateTime subject = DateTime.Now;
await Expect.That(subject).IsOnOrBefore(DateTime.Now).Within(TimeSpan.FromSeconds(1))
.Because("it should have taken less than one second");
```
```

## Between

Expand Down
2 changes: 1 addition & 1 deletion Source/aweXpect.Core/Results/BetweenResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class BetweenResult<TTarget, TType>(
Func<TType, TTarget> callback)
{
/// <summary>
/// …and <paramref name="maximum" />.
/// …and the <paramref name="maximum" /> value.
/// </summary>
public TTarget And(TType maximum)
=> callback(maximum);
Expand Down
108 changes: 108 additions & 0 deletions Source/aweXpect/That/DateOnlys/ThatDateOnly.IsBetween.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#if NET8_0_OR_GREATER
using System;
using aweXpect.Core;
using aweXpect.Core.Constraints;
using aweXpect.Customization;
using aweXpect.Helpers;
using aweXpect.Options;
using aweXpect.Results;

namespace aweXpect;

public static partial class ThatDateOnly
{
/// <summary>
/// Verifies that the subject is between the <paramref name="minimum" />…
/// </summary>
public static BetweenResult<TimeToleranceResult<DateOnly, IThat<DateOnly>>, DateOnly?> IsBetween(
this IThat<DateOnly> source,
DateOnly? minimum)
{
TimeTolerance tolerance = new();
return new BetweenResult<TimeToleranceResult<DateOnly, IThat<DateOnly>>, DateOnly?>(maximum
=> new TimeToleranceResult<DateOnly, IThat<DateOnly>>(
source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance)),
source,
tolerance));
}

/// <summary>
/// Verifies that the subject is not between the <paramref name="minimum" />…
/// </summary>
public static BetweenResult<TimeToleranceResult<DateOnly, IThat<DateOnly>>, DateOnly?> IsNotBetween(
this IThat<DateOnly> source,
DateOnly? minimum)
{
TimeTolerance tolerance = new();
return new BetweenResult<TimeToleranceResult<DateOnly, IThat<DateOnly>>, DateOnly?>(maximum
=> new TimeToleranceResult<DateOnly, IThat<DateOnly>>(
source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance).Invert()),
source,
tolerance));
}

private sealed class IsBetweenConstraint(
string it,
ExpectationGrammars grammars,
DateOnly? minimum,
DateOnly? maximum,
TimeTolerance tolerance)
: ConstraintResult.WithNotNullValue<DateOnly>(it, grammars),
IValueConstraint<DateOnly>
{
public ConstraintResult IsMetBy(DateOnly actual)
{
Actual = actual;
if (minimum is null || maximum is null)
{
Outcome = IsNegated ? Outcome.Success : Outcome.Failure;
}
else
{
TimeSpan timeTolerance = tolerance.Tolerance
?? Customize.aweXpect.Settings().DefaultTimeComparisonTolerance.Get();
if (IsNegated)
{
timeTolerance = timeTolerance.Negate();
}

Outcome = actual.AddDays((int)timeTolerance.TotalDays) >= minimum &&
actual.AddDays((int)timeTolerance.Negate().TotalDays) <= maximum
? Outcome.Success
: Outcome.Failure;
}

return this;
}

protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append("is between ");
ValueFormatters.Format(Formatter, stringBuilder, minimum);
stringBuilder.Append(" and ");
ValueFormatters.Format(Formatter, stringBuilder, maximum);
stringBuilder.Append(tolerance.ToDayString());
}

protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append(It).Append(" was ");
ValueFormatters.Format(Formatter, stringBuilder, Actual);
}

protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append("is not between ");
ValueFormatters.Format(Formatter, stringBuilder, minimum);
stringBuilder.Append(" and ");
ValueFormatters.Format(Formatter, stringBuilder, maximum);
stringBuilder.Append(tolerance.ToDayString());
}

protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
=> AppendNormalResult(stringBuilder, indentation);
}
}
#endif
112 changes: 112 additions & 0 deletions Source/aweXpect/That/DateOnlys/ThatNullableDateOnly.IsBetween.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#if NET8_0_OR_GREATER
using System;
using aweXpect.Core;
using aweXpect.Core.Constraints;
using aweXpect.Customization;
using aweXpect.Helpers;
using aweXpect.Options;
using aweXpect.Results;

namespace aweXpect;

public static partial class ThatNullableDateOnly
{
/// <summary>
/// Verifies that the subject is between the <paramref name="minimum" />…
/// </summary>
public static BetweenResult<TimeToleranceResult<DateOnly?, IThat<DateOnly?>>, DateOnly?> IsBetween(
this IThat<DateOnly?> source,
DateOnly? minimum)
{
TimeTolerance tolerance = new();
return new BetweenResult<TimeToleranceResult<DateOnly?, IThat<DateOnly?>>, DateOnly?>(maximum
=> new TimeToleranceResult<DateOnly?, IThat<DateOnly?>>(
source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance)),
source,
tolerance));
}

/// <summary>
/// Verifies that the subject is not between the <paramref name="minimum" />…
/// </summary>
public static BetweenResult<TimeToleranceResult<DateOnly?, IThat<DateOnly?>>, DateOnly?> IsNotBetween(
this IThat<DateOnly?> source,
DateOnly? minimum)
{
TimeTolerance tolerance = new();
return new BetweenResult<TimeToleranceResult<DateOnly?, IThat<DateOnly?>>, DateOnly?>(maximum
=> new TimeToleranceResult<DateOnly?, IThat<DateOnly?>>(
source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance).Invert()),
source,
tolerance));
}

private sealed class IsBetweenConstraint(
string it,
ExpectationGrammars grammars,
DateOnly? minimum,
DateOnly? maximum,
TimeTolerance tolerance)
: ConstraintResult.WithNotNullValue<DateOnly?>(it, grammars),
IValueConstraint<DateOnly?>
{
public ConstraintResult IsMetBy(DateOnly? actual)
{
Actual = actual;
if (actual is null && minimum is null && maximum is null)
{
Outcome = Outcome.Success;
}
else if (actual is null || minimum is null || maximum is null)
{
Outcome = IsNegated ? Outcome.Success : Outcome.Failure;
}
else
{
TimeSpan timeTolerance = tolerance.Tolerance
?? Customize.aweXpect.Settings().DefaultTimeComparisonTolerance.Get();
if (IsNegated)
{
timeTolerance = timeTolerance.Negate();
}

Outcome = actual.Value.AddDays((int)timeTolerance.TotalDays) >= minimum &&
actual.Value.AddDays((int)timeTolerance.Negate().TotalDays) <= maximum
? Outcome.Success
: Outcome.Failure;
}

return this;
}

protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append("is between ");
ValueFormatters.Format(Formatter, stringBuilder, minimum);
stringBuilder.Append(" and ");
ValueFormatters.Format(Formatter, stringBuilder, maximum);
stringBuilder.Append(tolerance.ToDayString());
}

protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append(It).Append(" was ");
ValueFormatters.Format(Formatter, stringBuilder, Actual);
}

protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append("is not between ");
ValueFormatters.Format(Formatter, stringBuilder, minimum);
stringBuilder.Append(" and ");
ValueFormatters.Format(Formatter, stringBuilder, maximum);
stringBuilder.Append(tolerance.ToDayString());
}

protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
=> AppendNormalResult(stringBuilder, indentation);
}
}
#endif
112 changes: 112 additions & 0 deletions Source/aweXpect/That/TimeOnlys/ThatNullableTimeOnly.IsBetween.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#if NET8_0_OR_GREATER
using System;
using aweXpect.Core;
using aweXpect.Core.Constraints;
using aweXpect.Customization;
using aweXpect.Helpers;
using aweXpect.Options;
using aweXpect.Results;

namespace aweXpect;

public static partial class ThatNullableTimeOnly
{
/// <summary>
/// Verifies that the subject is between the <paramref name="minimum" />…
/// </summary>
public static BetweenResult<TimeToleranceResult<TimeOnly?, IThat<TimeOnly?>>, TimeOnly?> IsBetween(
this IThat<TimeOnly?> source,
TimeOnly? minimum)
{
TimeTolerance tolerance = new();
return new BetweenResult<TimeToleranceResult<TimeOnly?, IThat<TimeOnly?>>, TimeOnly?>(maximum
=> new TimeToleranceResult<TimeOnly?, IThat<TimeOnly?>>(
source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance)),
source,
tolerance));
}

/// <summary>
/// Verifies that the subject is not between the <paramref name="minimum" />…
/// </summary>
public static BetweenResult<TimeToleranceResult<TimeOnly?, IThat<TimeOnly?>>, TimeOnly?> IsNotBetween(
this IThat<TimeOnly?> source,
TimeOnly? minimum)
{
TimeTolerance tolerance = new();
return new BetweenResult<TimeToleranceResult<TimeOnly?, IThat<TimeOnly?>>, TimeOnly?>(maximum
=> new TimeToleranceResult<TimeOnly?, IThat<TimeOnly?>>(
source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance).Invert()),
source,
tolerance));
}

private sealed class IsBetweenConstraint(
string it,
ExpectationGrammars grammars,
TimeOnly? minimum,
TimeOnly? maximum,
TimeTolerance tolerance)
: ConstraintResult.WithNotNullValue<TimeOnly?>(it, grammars),
IValueConstraint<TimeOnly?>
{
public ConstraintResult IsMetBy(TimeOnly? actual)
{
Actual = actual;
if (actual is null && minimum is null && maximum is null)
{
Outcome = Outcome.Success;
}
else if (actual is null || minimum is null || maximum is null)
{
Outcome = IsNegated ? Outcome.Success : Outcome.Failure;
}
else
{
TimeSpan timeTolerance = tolerance.Tolerance
?? Customize.aweXpect.Settings().DefaultTimeComparisonTolerance.Get();
if (IsNegated)
{
timeTolerance = timeTolerance.Negate();
}

Outcome = actual.Value.Add(timeTolerance) >= minimum &&
actual.Value.Add(timeTolerance.Negate()) <= maximum
? Outcome.Success
: Outcome.Failure;
}

return this;
}

protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append("is between ");
ValueFormatters.Format(Formatter, stringBuilder, minimum);
stringBuilder.Append(" and ");
ValueFormatters.Format(Formatter, stringBuilder, maximum);
stringBuilder.Append(tolerance);
}

protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append(It).Append(" was ");
ValueFormatters.Format(Formatter, stringBuilder, Actual);
}

protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append("is not between ");
ValueFormatters.Format(Formatter, stringBuilder, minimum);
stringBuilder.Append(" and ");
ValueFormatters.Format(Formatter, stringBuilder, maximum);
stringBuilder.Append(tolerance);
}

protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
=> AppendNormalResult(stringBuilder, indentation);
}
}
#endif
Loading