mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-04-23 10:42:37 -04:00
247 lines
9.2 KiB
C#
247 lines
9.2 KiB
C#
using System.Diagnostics;
|
|
using System.Text.RegularExpressions;
|
|
using Xunit;
|
|
|
|
namespace allstarr.Tests;
|
|
|
|
/// <summary>
|
|
/// Tests to validate JavaScript syntax in wwwroot files.
|
|
/// This prevents broken JavaScript from being committed.
|
|
/// </summary>
|
|
public class JavaScriptSyntaxTests
|
|
{
|
|
private readonly string _wwwrootPath;
|
|
|
|
public JavaScriptSyntaxTests()
|
|
{
|
|
// Get the path to the wwwroot directory
|
|
var projectRoot = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", ".."));
|
|
_wwwrootPath = Path.Combine(projectRoot, "allstarr", "wwwroot");
|
|
}
|
|
|
|
[Fact]
|
|
public void AppJs_ShouldHaveValidSyntax()
|
|
{
|
|
var filePath = Path.Combine(_wwwrootPath, "app.js");
|
|
Assert.True(File.Exists(filePath), $"app.js not found at {filePath}");
|
|
|
|
var isValid = ValidateJavaScriptSyntax(filePath, out var error);
|
|
Assert.True(isValid, $"app.js has syntax errors:\n{error}");
|
|
}
|
|
|
|
[Fact]
|
|
public void SpotifyMappingsJs_ShouldHaveValidSyntax()
|
|
{
|
|
var filePath = Path.Combine(_wwwrootPath, "spotify-mappings.js");
|
|
Assert.True(File.Exists(filePath), $"spotify-mappings.js not found at {filePath}");
|
|
|
|
var isValid = ValidateJavaScriptSyntax(filePath, out var error);
|
|
Assert.True(isValid, $"spotify-mappings.js has syntax errors:\n{error}");
|
|
}
|
|
|
|
[Fact]
|
|
public void ModularJs_UtilsShouldHaveValidSyntax()
|
|
{
|
|
var filePath = Path.Combine(_wwwrootPath, "js", "utils.js");
|
|
Assert.True(File.Exists(filePath), $"js/utils.js not found at {filePath}");
|
|
|
|
var isValid = ValidateJavaScriptSyntax(filePath, out var error);
|
|
Assert.True(isValid, $"js/utils.js has syntax errors:\n{error}");
|
|
}
|
|
|
|
[Fact]
|
|
public void ModularJs_ApiShouldHaveValidSyntax()
|
|
{
|
|
var filePath = Path.Combine(_wwwrootPath, "js", "api.js");
|
|
Assert.True(File.Exists(filePath), $"js/api.js not found at {filePath}");
|
|
|
|
var isValid = ValidateJavaScriptSyntax(filePath, out var error);
|
|
Assert.True(isValid, $"js/api.js has syntax errors:\n{error}");
|
|
}
|
|
|
|
[Fact]
|
|
public void ModularJs_MainShouldHaveValidSyntax()
|
|
{
|
|
var filePath = Path.Combine(_wwwrootPath, "js", "main.js");
|
|
Assert.True(File.Exists(filePath), $"js/main.js not found at {filePath}");
|
|
|
|
var isValid = ValidateJavaScriptSyntax(filePath, out var error);
|
|
Assert.True(isValid, $"js/main.js has syntax errors:\n{error}");
|
|
}
|
|
|
|
[Fact]
|
|
public void ModularJs_ExtractedModulesShouldHaveValidSyntax()
|
|
{
|
|
var moduleFiles = new[]
|
|
{
|
|
"settings-editor.js",
|
|
"auth-session.js",
|
|
"dashboard-data.js",
|
|
"operations.js",
|
|
"playlist-admin.js",
|
|
"scrobbling-admin.js"
|
|
};
|
|
|
|
foreach (var moduleFile in moduleFiles)
|
|
{
|
|
var filePath = Path.Combine(_wwwrootPath, "js", moduleFile);
|
|
Assert.True(File.Exists(filePath), $"js/{moduleFile} not found at {filePath}");
|
|
|
|
var isValid = ValidateJavaScriptSyntax(filePath, out var error);
|
|
Assert.True(isValid, $"js/{moduleFile} has syntax errors:\n{error}");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void AppJs_ShouldBeDeprecated()
|
|
{
|
|
var filePath = Path.Combine(_wwwrootPath, "app.js");
|
|
var content = File.ReadAllText(filePath);
|
|
|
|
// Check that the file is now just a deprecation notice
|
|
Assert.Contains("DEPRECATED", content);
|
|
Assert.Contains("main.js", content);
|
|
}
|
|
|
|
[Fact]
|
|
public void MainJs_ShouldBeComplete()
|
|
{
|
|
var mainPath = Path.Combine(_wwwrootPath, "js", "main.js");
|
|
var dashboardPath = Path.Combine(_wwwrootPath, "js", "dashboard-data.js");
|
|
var settingsPath = Path.Combine(_wwwrootPath, "js", "settings-editor.js");
|
|
var authPath = Path.Combine(_wwwrootPath, "js", "auth-session.js");
|
|
var operationsPath = Path.Combine(_wwwrootPath, "js", "operations.js");
|
|
var playlistPath = Path.Combine(_wwwrootPath, "js", "playlist-admin.js");
|
|
var scrobblingPath = Path.Combine(_wwwrootPath, "js", "scrobbling-admin.js");
|
|
|
|
Assert.True(File.Exists(mainPath), $"js/main.js not found at {mainPath}");
|
|
Assert.True(File.Exists(dashboardPath), $"js/dashboard-data.js not found at {dashboardPath}");
|
|
Assert.True(File.Exists(settingsPath), $"js/settings-editor.js not found at {settingsPath}");
|
|
Assert.True(File.Exists(authPath), $"js/auth-session.js not found at {authPath}");
|
|
Assert.True(File.Exists(operationsPath), $"js/operations.js not found at {operationsPath}");
|
|
Assert.True(File.Exists(playlistPath), $"js/playlist-admin.js not found at {playlistPath}");
|
|
Assert.True(File.Exists(scrobblingPath), $"js/scrobbling-admin.js not found at {scrobblingPath}");
|
|
|
|
var mainContent = File.ReadAllText(mainPath);
|
|
var dashboardContent = File.ReadAllText(dashboardPath);
|
|
var settingsContent = File.ReadAllText(settingsPath);
|
|
var authContent = File.ReadAllText(authPath);
|
|
var operationsContent = File.ReadAllText(operationsPath);
|
|
var playlistContent = File.ReadAllText(playlistPath);
|
|
var scrobblingContent = File.ReadAllText(scrobblingPath);
|
|
|
|
Assert.Contains("DOMContentLoaded", mainContent);
|
|
Assert.Contains("authSession.bootstrapAuth()", mainContent);
|
|
Assert.Contains("initDashboardData", mainContent);
|
|
|
|
Assert.Contains("window.fetchStatus", dashboardContent);
|
|
Assert.Contains("window.fetchPlaylists", dashboardContent);
|
|
Assert.Contains("window.fetchConfig", dashboardContent);
|
|
Assert.Contains("window.fetchEndpointUsage", dashboardContent);
|
|
|
|
Assert.Contains("window.openEditSetting", settingsContent);
|
|
Assert.Contains("window.saveEditSetting", settingsContent);
|
|
Assert.Contains("window.logoutAdminSession", authContent);
|
|
Assert.Contains("window.restartContainer", operationsContent);
|
|
Assert.Contains("window.linkPlaylist", playlistContent);
|
|
Assert.Contains("window.loadScrobblingConfig", scrobblingContent);
|
|
}
|
|
|
|
[Fact]
|
|
public void AppJs_ShouldHaveBalancedBraces()
|
|
{
|
|
// app.js is now deprecated and just contains comments
|
|
// Skip this test or check main.js instead
|
|
var filePath = Path.Combine(_wwwrootPath, "js", "main.js");
|
|
var content = File.ReadAllText(filePath);
|
|
|
|
var openBraces = content.Count(c => c == '{');
|
|
var closeBraces = content.Count(c => c == '}');
|
|
|
|
Assert.Equal(openBraces, closeBraces);
|
|
}
|
|
|
|
[Fact]
|
|
public void AppJs_ShouldHaveBalancedParentheses()
|
|
{
|
|
// app.js is now deprecated and just contains comments
|
|
// Skip this test or check main.js instead
|
|
var filePath = Path.Combine(_wwwrootPath, "js", "main.js");
|
|
|
|
// Use Node.js to validate syntax instead of counting parentheses
|
|
// This is more reliable than regex-based string/comment removal
|
|
string error;
|
|
var isValid = ValidateJavaScriptSyntax(filePath, out error);
|
|
|
|
Assert.True(isValid, $"JavaScript syntax validation failed: {error}");
|
|
}
|
|
|
|
[Fact]
|
|
public void ApiJs_ShouldCentralizeFetchHandling()
|
|
{
|
|
var filePath = Path.Combine(_wwwrootPath, "js", "api.js");
|
|
var content = File.ReadAllText(filePath);
|
|
|
|
Assert.Contains("async function requestJson", content);
|
|
Assert.Contains("async function requestBlob", content);
|
|
Assert.Contains("async function requestOptionalJson", content);
|
|
|
|
var fetchCallCount = Regex.Matches(content, @"\bfetch\(").Count;
|
|
Assert.Equal(3, fetchCallCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void ScrobblingAdmin_ShouldUseApiWrappersInsteadOfDirectFetch()
|
|
{
|
|
var filePath = Path.Combine(_wwwrootPath, "js", "scrobbling-admin.js");
|
|
var content = File.ReadAllText(filePath);
|
|
|
|
Assert.DoesNotContain("fetch(", content);
|
|
Assert.Contains("API.fetchScrobblingStatus()", content);
|
|
Assert.Contains("API.updateLocalTracksScrobbling", content);
|
|
Assert.Contains("API.authenticateLastFm()", content);
|
|
Assert.Contains("API.validateListenBrainzToken", content);
|
|
}
|
|
|
|
private bool ValidateJavaScriptSyntax(string filePath, out string error)
|
|
{
|
|
error = string.Empty;
|
|
|
|
try
|
|
{
|
|
// Use Node.js to check syntax
|
|
var process = new Process
|
|
{
|
|
StartInfo = new ProcessStartInfo
|
|
{
|
|
FileName = "node",
|
|
Arguments = $"--check \"{filePath}\"",
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true
|
|
}
|
|
};
|
|
|
|
process.Start();
|
|
var stderr = process.StandardError.ReadToEnd();
|
|
process.WaitForExit();
|
|
|
|
if (process.ExitCode != 0)
|
|
{
|
|
error = stderr;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
error = $"Failed to run Node.js syntax check: {ex.Message}\n" +
|
|
"Make sure Node.js is installed and available in PATH.";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|