Display Script Testing
Fig provides a comprehensive testing framework that allows you to test your display scripts offline, without needing to run the full Fig web application. This is particularly useful for automated testing, CI/CD pipelines, and rapid development iterations.
This guide covers testing Fig display scripts (JavaScript logic). If you're looking to test ASP.NET Core applications that use Fig for configuration, see the Integration Testing guide which covers testing your application with Fig configuration providers.
Overview
The Fig Client Testing framework enables you to:
- Test display scripts in isolation without running Fig.Web
- Validate script logic against different setting configurations
- Run automated tests in CI/CD pipelines
- Debug script behavior more easily
- Ensure script reliability before deployment
The framework supports two approaches:
- Settings Class Approach (Recommended): Use your actual
SettingsBaseclasses for type-safe testing with full attribute support - Manual Configuration: Manually configure individual settings when needed
Getting Started
Prerequisites
Before you can test your display scripts, you need to add the Fig Client Testing package to your test project:
<PackageReference Include="Fig.Client.Testing" Version="latest" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
The Fig.Client.Testing package provides testing utilities for both display script testing (covered in this guide) and integration testing of ASP.NET Core applications. You only need to install this package once per test project to support both testing scenarios.
Recommended Approach: Using Settings Classes
The preferred way to test display scripts is by using your actual SettingsBase classes. This approach provides:
- Type Safety: Compile-time checking of property names and types
- Attribute Preservation: Automatic handling of validation, categories, secrets, etc.
- Maintainability: Changes to settings automatically reflect in tests
- Real-world Accuracy: Tests mirror actual client configuration
Basic Settings Class Test
using Fig.Client;
using Fig.Client.Attributes;
using Fig.Client.Testing;
using NUnit.Framework;
// Define your settings class with display scripts as constants
public class ApiSettings : SettingsBase
{
// Display Script Constants
private const string HttpsValidationScript = @"
if (RequireHttps.Value && !BaseUrl.Value.toString().startsWith('https://')) {
BaseUrl.ValidationExplanation = 'HTTPS is required when RequireHttps is enabled';
BaseUrl.IsValid = false;
} else {
BaseUrl.ValidationExplanation = '';
BaseUrl.IsValid = true;
}";
private const string DebugVisibilityScript = @"
if (DebugLogging.Value) {
DatabaseConnection.IsVisible = true;
DatabaseConnection.CategoryName = 'Debug';
DatabaseConnection.CategoryColor = '#FF9800';
} else {
DatabaseConnection.IsVisible = false;
}";
public override string ClientDescription => "API Configuration";
[Setting("The base URL for the API")]
[DisplayScript(HttpsValidationScript)]
public string BaseUrl { get; set; } = "https://api.example.com";
[Setting("Enable HTTPS requirement")]
public bool RequireHttps { get; set; } = true;
[Setting("API timeout in seconds")]
[Validation(@"^\d+$", "Must be a positive number")]
public int TimeoutSeconds { get; set; } = 30;
[Setting("Database connection string")]
[Secret]
[DisplayScript(DebugVisibilityScript)]
public string DatabaseConnection { get; set; } = "Server=localhost;";
[Setting("Enable debug logging")]
[Advanced]
public bool DebugLogging { get; set; } = false;
// Public methods to expose scripts for testing
public static string GetHttpsValidationScript() => HttpsValidationScript;
public static string GetDebugVisibilityScript() => DebugVisibilityScript;
public override IEnumerable<string> GetValidationErrors() => new List<string>();
}
[TestFixture]
public class ApiSettingsTests
{
[Test]
public void Should_Validate_HTTPS_Requirement()
{
// Arrange
var settings = new ApiSettings();
var testRunner = new ClientTestRunner();
// Act
var testClient = testRunner.CreateClient(settings)
.WithSetting("RequireHttps", true)
.WithSetting("BaseUrl", "http://insecure.example.com")
.Build();
// Use the script defined in the settings class
testRunner.ExecuteScript(testClient, ApiSettings.GetHttpsValidationScript());
// Assert
var baseUrlSetting = testClient.GetSetting("BaseUrl");
Assert.IsFalse(baseUrlSetting.IsValid);
Assert.That(baseUrlSetting.ValidationExplanation, Contains.Substring("HTTPS is required"));
}
[Test]
public void Should_Show_Debug_Settings_When_Enabled()
{
// Arrange
var settings = new ApiSettings();
var testRunner = new ClientTestRunner();
// Act
var testClient = testRunner.CreateClient(settings)
.WithSetting("DebugLogging", true)
.Build();
// Use the debug visibility script from the settings class
testRunner.ExecuteScript(testClient, ApiSettings.GetDebugVisibilityScript());
// Assert
var dbSetting = testClient.GetSetting("DatabaseConnection");
Assert.IsTrue(dbSetting.IsVisible);
Assert.That(dbSetting.CategoryName, Is.EqualTo("Debug"));
}
}
Advanced Settings Class Examples
Complex Validation Logic
public class ApiSettings : SettingsBase
{
private const string ComplexValidationScript = @"
// Multi-field validation
if (RequireHttps.Value && !BaseUrl.Value.toString().startsWith('https://')) {
BaseUrl.ValidationExplanation = 'HTTPS required when RequireHttps is enabled';
BaseUrl.IsValid = false;
}
// Range validation
if (TimeoutSeconds.Value < 10) {
TimeoutSeconds.ValidationExplanation = 'Minimum timeout is 10 seconds';
TimeoutSeconds.IsValid = false;
}";
[Setting("The base URL for the API")]
[DisplayScript(ComplexValidationScript)]
public string BaseUrl { get; set; } = "https://api.example.com";
[Setting("API timeout in seconds")]
[DisplayScript(ComplexValidationScript)]
public int TimeoutSeconds { get; set; } = 30;
// ... other properties ...
public static string GetComplexValidationScript() => ComplexValidationScript;
}
[Test]
public void Should_Apply_Complex_Validation_Rules()
{
var settings = new ApiSettings();
var testRunner = new ClientTestRunner();
var testClient = testRunner.CreateClient(settings)
.WithSetting("RequireHttps", true)
.WithSetting("BaseUrl", "http://insecure.com")
.WithSetting("TimeoutSeconds", 5)
.Build();
// Use the validation script defined in the settings class
testRunner.ExecuteScript(testClient, ApiSettings.GetComplexValidationScript());
Assert.IsFalse(testClient.GetSetting("BaseUrl").IsValid);
Assert.IsFalse(testClient.GetSetting("TimeoutSeconds").IsValid);
}
Conditional Visibility and Categories
public class ApiSettings : SettingsBase
{
private const string ProductionModeScript = @"
var isProduction = !DebugLogging.Value;
if (isProduction) {
// Hide sensitive settings in production
DatabaseConnection.IsVisible = false;
DatabaseConnection.Advanced = true;
// Organize by priority
RequireHttps.DisplayOrder = 1;
BaseUrl.DisplayOrder = 2;
TimeoutSeconds.DisplayOrder = 3;
// Production category styling
RequireHttps.CategoryName = 'Production';
RequireHttps.CategoryColor = '#E91E63';
}";
[Setting("Enable debug logging")]
[Advanced]
[DisplayScript(ProductionModeScript)]
public bool DebugLogging { get; set; } = false;
// ... other properties ...
public static string GetProductionModeScript() => ProductionModeScript;
}
[Test]
public void Should_Control_Setting_Visibility_And_Organization()
{
var settings = new ApiSettings();
var testRunner = new ClientTestRunner();
var testClient = testRunner.CreateClient(settings)
.WithSetting("DebugLogging", false)
.Build();
// Use the production mode script from the settings class
testRunner.ExecuteScript(testClient, ApiSettings.GetProductionModeScript());
var dbSetting = testClient.GetSetting("DatabaseConnection");
var httpsSetting = testClient.GetSetting("RequireHttps");
Assert.IsFalse(dbSetting.IsVisible);
Assert.IsTrue(dbSetting.Advanced);
Assert.That(httpsSetting.DisplayOrder, Is.EqualTo(1));
Assert.That(httpsSetting.CategoryName, Is.EqualTo("Production"));
}
TimeSpan and Type Handling
public class ApiSettings : SettingsBase
{
private const string TimeoutConversionScript = @"
// TimeSpan values are provided as milliseconds in scripts
var timeoutInSeconds = RequestTimeout.Value / 1000;
if (timeoutInSeconds > 60) {
TimeoutSeconds.Value = timeoutInSeconds;
}";
[Setting("Request timeout")]
[DisplayScript(TimeoutConversionScript)]
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
[Setting("API timeout in seconds")]
public int TimeoutSeconds { get; set; } = 30;
// ... other properties ...
public static string GetTimeoutConversionScript() => TimeoutConversionScript;
}
[Test]
public void Should_Handle_TimeSpan_Settings()
{
var settings = new ApiSettings();
var testRunner = new ClientTestRunner();
var testClient = testRunner.CreateClient(settings)
.WithSetting("RequestTimeout", TimeSpan.FromMinutes(2))
.Build();
// Use the timeout conversion script from the settings class
testRunner.ExecuteScript(testClient, ApiSettings.GetTimeoutConversionScript());
var timeoutValue = testClient.GetSetting("TimeoutSeconds").GetValue();
Assert.That(timeoutValue, Is.EqualTo(120)); // 2 minutes = 120 seconds
}
Alternative Approach: Manual Configuration
When you need more control or don't have a settings class available, you can manually configure settings:
Basic Manual Test Structure
[TestFixture]
public class ManualDisplayScriptTests
{
[Test]
public void Should_Hide_Setting_When_Condition_Is_False()
{
// Arrange
var testRunner = new DisplayScriptTestRunner();
var client = testRunner.CreateTestClient("TestClient")
.AddBoolSetting("EnableFeature", false)
.AddStringSetting("FeatureConfig", "default");
var script = @"
if (!EnableFeature.Value) {
FeatureConfig.IsVisible = false;
}
";
// Act
testRunner.RunScript(script, client);
// Assert
Assert.IsFalse(client.GetSetting("FeatureConfig").IsVisible);
}
}
Manual Setting Types
The testing framework supports all Fig setting types when using manual configuration: