0

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.

  1. Create a mocked Item and attempt to save changes to it.
  2. 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
8
  • Replace screenshots with code snippets. For SEO reasons and for the people who want to help you with your issue. No one is going to re-type manually code from screeenshots if they want to try things on their own Jan 24 at 8:13
  • Thanks @MarekMusielak, updated with snippets. Jan 24 at 10:34
  • I question your approach entirely. For one; you shouldn't be attempting to unit test the framework (Sitecore), it doesn't serve any useful purpose. Secondly it appears you are trying to make sure in your validator that thumbnails have been assigned to certain template types - this is developer controlled territory. Developers are typically admin users, making the entire point of the validator fail. I would suggest you come at this at an integration level, make your required check on the serialised files on disk and make it part of your PR process.
    – Mark Cassidy
    Jan 24 at 10:38
  • Hi @MarkCassidy thanks for the feedback. My intention here is to ensure that should Sitecore change its implementation of the RequiredFieldValidator in future, my tests would highlight that the expected functionality is now broken. As the Appearance template is used globally across all items I want to ensure my validator executes in the context of one of those rendering items. Working in a large team of developers I would not trust them to ensure they are applying these thumbnails manually nor via a PR code review. Unless you are suggesting an automated step within the PR process? Jan 24 at 11:04
  • If it is truly that important then yes, you could automate this in the PR process. This problem can be entirely analysed from the serialised assets that would be part of any PR. Would require a little bit of PS scripting but end up being a much cleaner and relevant approach.
    – Mark Cassidy
    Jan 24 at 11:55

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.