I have implemented my own field validator that ensures a thumbnail image is always set on a rendering item. Working example:
[Serializable]
public class MandatoryThumbnailFieldValidator : RequiredFieldValidator
{
private readonly IEnumerable<ID> _renderingsWithMandatoryThumbnails;
public MandatoryThumbnailFieldValidator()
{
var controllerRenderingId = new ID("{2A3E91A0-7987-44B5-AB34-35C2D9DE83B9}");
var viewRenderingId = new ID("{99F8905D-4A87-4EB8-9F8B-A9BEBFB3ADD6}");
var javascriptRenderingId = new ID("{B1C80C94-792D-44DA-861E-557C6E1915F2}");
var jsonRenderingId = new ID("{04646A89-996F-4EE7-878A-FFDBF1F0EF0D}");
_renderingsWithMandatoryThumbnails = new List<ID> { controllerRenderingId, viewRenderingId,
javascriptRenderingId, jsonRenderingId };
}
public MandatoryThumbnailFieldValidator(SerializationInfo info, StreamingContext context) : base(info, context)
{ }
protected override ValidatorResult Evaluate()
{
var currentTemplateId = this.GetItem().TemplateID;
if (!_renderingsWithMandatoryThumbnails.Contains(currentTemplateId))
return ValidatorResult.Valid;
var validatorResult = base.Evaluate();
// Required Field Validator returns CriticalError which provides validation prompt, but allows save.
// Upgrading CriticalError to FatalError which also prevents save.
validatorResult = validatorResult == ValidatorResult.CriticalError
? ValidatorResult.FatalError
: validatorResult;
return validatorResult;
}
protected override ValidatorResult GetMaxValidatorResult() => this.GetFailedResult(ValidatorResult.FatalError);
protected override bool HandleCriticalError() => this.MaxValidatorResult != ValidatorResult.FatalError;
public override string Name => "Mandatory Thumbnail";
}
I have inherited from the OOTB RequiredFieldValidator & called the base validation when the context item is a rendering item. Additionally I want to prevent the saving of the item so have upgraded the ValidationResult from CriticalError to FatalError.
Having applied my custom validator to the validation rules of the thumbnail field within the appearance template, this is all working as expected.
So now I am looking to implement my unit tests to achieve code coverage & prevent any other dev's inadvertently breaking this functionality.
The BaseValidator abstract class contains the following method:
protected virtual Item GetItem()
{
if (this.itemUri == (ItemUri) null)
return (Item) null;
Item obj = Database.GetItem(this.itemUri);
if (obj == null || obj.Versions.Count == 0)
return (Item) null;
this.UpdateItem(obj);
return obj;
}
Which in turn calls this method within the Database abstract class:
public static Item GetItem(ItemUri uri)
{
Assert.ArgumentNotNull((object) uri, nameof (uri));
return Factory.GetDatabase(uri.DatabaseName)?.GetItem(uri.ToDataUri());
}
My problem being that Factory is a static class and it is trying to retrieve the current database context. I have attempted to set the DataUri property but this is gradually uncovering subsequent issues which now has me questioning whether I am adopting the best approach.
I assume there are 2 approaches I could take.
- Create a mocked Item and attempt to save changes to it.
- Directly call my validators Validate method.
I have briefly looked into FakeDb but have not discovered any obvious ways to achieve my desired outcome.
Can anyone give me some pointers as to the best approach here?
Tests I'll be looking to implement:
- Non Rendering Item returns Valid ValidationResult
- Non Rendering Item with empty thumbnail field returns Valid ValidationResult
- Rendering Item with populated thumbnail field returns Valid ValidationResult
- Rendering Item with empty thumbnail field returns FatalError ValidationResult