DocSpace-client/common/Tests/Frontend.Translations.Tests/ImagesTest.cs
2024-03-18 03:13:02 +04:00

375 lines
14 KiB
C#

// (c) Copyright Ascensio System SIA 2010-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Frontend.Tests;
using Frontend.Tests.Models;
using Microsoft.VisualBasic;
using NUnit.Framework;
namespace Frontend.Tests;
public class ImagesTest
{
public static string BasePath
{
get
{
return Environment.GetEnvironmentVariable("BASE_DIR") ?? Path.GetFullPath(Utils.ConvertPathToOS("../../../../../../"));
}
}
public List<string> Workspaces { get; set; }
public List<ImageFile> ImageFiles { get; set; }
public List<SourceImageFile> SourceImageFiles { get; set; }
public Dictionary<string, string> HashErrorFiles { get; set; }
private static Dictionary<ModuleTypes, string> Modules
{
get
{
return new Dictionary<ModuleTypes, string>() {
{ ModuleTypes.PUBLIC, Path.Combine(BasePath,Utils.ConvertPathToOS("public")) },
{ ModuleTypes.SHARED, Path.Combine(BasePath,Utils.ConvertPathToOS("packages/shared")) },
{ ModuleTypes.CLIENT, Path.Combine(BasePath,Utils.ConvertPathToOS("packages/client")) },
{ ModuleTypes.EDITOR, Path.Combine(BasePath,Utils.ConvertPathToOS("packages/doceditor")) },
{ ModuleTypes.LOGIN, Path.Combine(BasePath,Utils.ConvertPathToOS("packages/login")) },
{ ModuleTypes.MANAGEMENT, Path.Combine(BasePath,Utils.ConvertPathToOS("packages/management")) }
};
}
}
private static ModuleTypes GetModuleType(string path)
{
var mType = Modules.First(m => path.Contains(m.Value)).Key;
return mType;
}
[OneTimeSetUp]
public void Setup()
{
TestContext.Progress.WriteLine($"Base path = {BasePath}");
HashErrorFiles = new Dictionary<string, string>();
Workspaces = Modules.Values.ToList();
TestContext.Progress.WriteLine($"Workspaces: {string.Join("\r\n", Workspaces)}");
//var searchPaterns = new string[] { "*.svg", "*.png", "*.jpg", "*.ico", "*.jpeg" };
var imageSearchPatern = @"\.svg|\.png|\.jpg|\.ico|\.jpeg";
var imageFiles = from wsPath in Workspaces
from filePath in Utils.GetFiles(wsPath, imageSearchPatern, SearchOption.AllDirectories)
where !filePath.Contains(Utils.ConvertPathToOS("dist/")) && !filePath.Contains(Utils.ConvertPathToOS("tests/"))
//where filePath.Contains(Utils.ConvertPathToOS("public/images/"))
select Path.GetFullPath(filePath);
//TestContext.Progress.WriteLine($"Found imageFiles by filter '{imageSearchPatern}' count={imageFiles.Count()}. First path is '{imageFiles.FirstOrDefault()}'");
ImageFiles = new List<ImageFile>();
foreach (var path in imageFiles)
{
try
{
using (var md5 = MD5.Create())
{
using (var stream = File.OpenRead(path))
{
var hash = md5.ComputeHash(stream);
var md5hash = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
ImageFiles.Add(new ImageFile(path, GetModuleType(path), md5hash));
}
}
}
catch (Exception ex)
{
HashErrorFiles.Add(path, ex.Message);
TestContext.Progress.WriteLine($"File path = {path} failed to parse with error: {ex.Message}");
}
}
TestContext.Progress.WriteLine($"Found ImageFiles by filter '{imageSearchPatern}' count={ImageFiles.Count}. First path is '{ImageFiles.FirstOrDefault()?.FilePath}'");
var sourceSearchPatern = @"(\.js|\.jsx|\.ts|\.tsx|\.html|\.css|\.scss|\.sass)$";
var sourceFiles = (from wsPath in Workspaces
from filePath in Utils.GetFiles(wsPath, sourceSearchPatern, SearchOption.AllDirectories)
where !filePath.Contains(Utils.ConvertPathToOS("dist/"))
&& !filePath.Contains(Utils.ConvertPathToOS("tests/"))
&& !filePath.Contains(".test.js")
&& !filePath.Contains(".stories.js")
&& !filePath.Contains(".test.ts")
&& !filePath.Contains(".stories.ts")
&& !filePath.Contains(".test.tsx")
&& !filePath.Contains(".stories.tsx")
select Utils.ConvertPathToOS(filePath))
.ToList();
//TestContext.Progress.WriteLine($"Found sourceFiles by '{searchPatern}' filter = {sourceFiles.Count()}. First path is '{sourceFiles.FirstOrDefault()}'");
SourceImageFiles = new List<SourceImageFile>();
var pattern = $"\"([a-zA-Z0-9_.:/-]+({imageSearchPatern}))\"";
var regexp = new Regex(pattern, RegexOptions.Multiline | RegexOptions.ECMAScript);
foreach (var path in sourceFiles)
{
var sourceText = File.ReadAllText(path);
var mType = GetModuleType(path);
var matches = regexp.Matches(sourceText);
var images = matches
.Select(m => m.Groups[1].Success
? m.Groups[1].Value
: null)
.Where(m => m != null)
.Distinct()
.Select(p => new ImageFile(p, mType))
.ToList();
if (!images.Any())
continue;
var sourceImageFile = new SourceImageFile(path, mType);
sourceImageFile.Images = images;
SourceImageFiles.Add(sourceImageFile);
}
TestContext.Progress.WriteLine($"Found SourceImageFiles by filter '{sourceSearchPatern}' " +
$"count={SourceImageFiles.Count}. First path is '{SourceImageFiles.FirstOrDefault()?.Path}'");
}
[Test]
[Category("Images")]
public void ParseMd5Test()
{
Assert.AreEqual(0, HashErrorFiles.Count, string.Join("\r\n",
HashErrorFiles.Select(e => $"File path = '{e.Key}' failed to parse with error: '{e.Value}'")));
}
[Test]
[Category("Images")]
public void DublicatesFilesByMD5HashTest()
{
var duplicatesByMD5 = ImageFiles
.GroupBy(t => t.Md5Hash)
.Where(grp => grp.Count() > 1)
.Select(grp => new { Key = grp.Key, Count = grp.Count(), Paths = grp.ToList().Select(f => f.FilePath) })
.OrderByDescending(itm => itm.Count)
.ToList();
Assert.AreEqual(0, duplicatesByMD5.Count, "Dublicates by MD5 hash:\r\n" +
string.Join("\r\n", duplicatesByMD5.Select(d => $"\r\nMD5='{d.Key}':\r\n" +
$"{string.Join("\r\n", d.Paths.Select(p => p))}'")));
}
[Test]
[Category("Images")]
public void DublicatesFilesByFileNameButDifferentByMD5HashTest()
{
var duplicatesByNameWithDifMD5 = ImageFiles
.Where(i => !i.FilePath.Contains(Utils.ConvertPathToOS("public/images/icons/")))
.GroupBy(t => t.FileName)
.Where(grp => grp.Count() > 1)
.Select(grp => new { Key = grp.Key, Count = grp.Count(), Images = grp.ToList() })
.Where(g => g.Images.Any(i => i.Md5Hash != g.Images[0].Md5Hash))
.ToList();
Assert.AreEqual(0, duplicatesByNameWithDifMD5.Count, "Dublicates by Name but different by MD5 hashs:\r\n" +
string.Join("\r\n", duplicatesByNameWithDifMD5.Select(d => $"\r\nFileName='{d.Key}':\r\n" +
$"{string.Join("\r\n", d.Images.Select(p => $"MD5='{p.Md5Hash}' Path='{p.FilePath}'"))}")));
}
[Test]
[Category("Images")]
public void UselessImagesTest()
{
var usedImages = SourceImageFiles
.SelectMany(item => item.Images.Select(p => p.FileName))
.Distinct()
.ToList();
var existingImages = ImageFiles
.Select(j => j.FileName)
.Distinct()
.ToList();
var notFoundInSourceImages = existingImages.Except(usedImages).ToList();
var notFoundPaths = ImageFiles
.Where(i => notFoundInSourceImages.Contains(i.FileName))
.Select(i => i.FilePath)
.ToList();
Assert.AreEqual(0, notFoundPaths.Count,
"Some images are not used in source files by name:\r\n{0}",
string.Join("\r\n", notFoundPaths));
}
private static string Capitalize(string str)
{
if (str.Length == 0)
return str;
if (str.Length == 1)
return char.ToUpper(str[0]) + "";
return char.ToUpper(str[0]) + str.Substring(1);
}
private static string GetVariableByName(string name)
{
var split = name.Split(new char[] { '.', '_', '-' });
return (split.Length == 1
? Capitalize(split[0])
: string.Join("", split.Select(s => Capitalize(s)))
) + "Url";
}
[Test]
[Ignore("Ignore a fixture")]
public void FixStaticTest()
{
var onlyJsFiles = SourceImageFiles
.Where(f => f.ModuleType == ModuleTypes.CLIENT && f.Path.EndsWith(".js"))
.ToList();
foreach (var f in onlyJsFiles)
{
var dictionary = new Dictionary<string, string>();
foreach (var i in f.Images.Where(i => i.FilePath.StartsWith("/static/images")))
{
dictionary.TryAdd(i.FilePath, GetVariableByName(i.FileName));
}
if (!dictionary.Any())
continue;
var content = File.ReadAllText(f.Path);
var sb = new StringBuilder();
foreach (var item in dictionary)
{
content = content.Replace($"=\"{item.Key}\"", "={" + item.Value + "}");
content = content.Replace($"\"{item.Key}\"", item.Value);
var query = item.Key.EndsWith(".svg") ? "?url" : "";
sb.AppendLine($"import {item.Value} from \"{item.Key.Replace("/static/images", "PUBLIC_DIR/images")}{query}\";");
}
content = sb.ToString() + content;
File.WriteAllText(f.Path, content, Encoding.UTF8);
}
foreach (var f in onlyJsFiles)
{
var dictionary = new Dictionary<string, string>();
foreach (var i in f.Images.Where(i => i.FilePath.StartsWith("static/images")))
{
dictionary.TryAdd(i.FilePath, GetVariableByName(i.FileName));
}
if (!dictionary.Any())
continue;
var content = File.ReadAllText(f.Path);
var sb = new StringBuilder();
foreach (var item in dictionary)
{
content = content.Replace($"=\"{item.Key}\"", "={" + item.Value + "}");
content = content.Replace($"\"{item.Key}\"", item.Value);
var query = item.Key.EndsWith(".svg") ? "?url" : "";
sb.AppendLine($"import {item.Value} from \"{item.Key.Replace("static/images", "PUBLIC_DIR/images")}{query}\";");
}
content = sb.ToString() + content;
File.WriteAllText(f.Path, content, Encoding.UTF8);
}
foreach (var f in onlyJsFiles)
{
var dictionary = new Dictionary<string, string>();
foreach (var i in f.Images.Where(i => i.FilePath.StartsWith("images/")))
{
dictionary.TryAdd(i.FilePath, GetVariableByName(i.FileName));
}
if (!dictionary.Any())
continue;
var content = File.ReadAllText(f.Path);
var sb = new StringBuilder();
foreach (var item in dictionary)
{
content = content.Replace($"=\"{item.Key}\"", "={" + item.Value + "}");
content = content.Replace($"\"{item.Key}\"", item.Value);
var query = item.Key.EndsWith(".svg") ? "?url" : "";
sb.AppendLine($"import {item.Value} from \"{item.Key.Replace("images", "ASSETS_DIR/images")}{query}\";");
}
content = sb.ToString() + content;
File.WriteAllText(f.Path, content, Encoding.UTF8);
}
Assert.AreEqual(0, 0);
}
}