Skip to content

Commit 61e18bd

Browse files
Merge pull request #38 from magiccodingman/magiccodingman/jsonfix
Json Serialization Refactor (Removing Newtonsoft & Fixing NotMapped being serialized)
2 parents 788e6b5 + 63a7a17 commit 61e18bd

39 files changed

+1366
-49
lines changed

E2eTest/E2eTest.csproj

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<LangVersion>latest</LangVersion>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.2" />
12+
<PackageReference Include="Microsoft.Playwright.MSTest" Version="1.49.0" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
14+
<PackageReference Include="MSTest" Version="3.6.4" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\E2eTestWebApp\E2eTestWebApp.csproj" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<Using Include="Microsoft.Playwright.MSTest" />
23+
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
24+
<Using Include="System.Text.RegularExpressions" />
25+
<Using Include="System.Threading.Tasks" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<None Update="SingleRecordBasicTest.cs.js">
30+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
31+
</None>
32+
<None Update="OpenTest.cs.js">
33+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
34+
</None>
35+
</ItemGroup>
36+
37+
</Project>

E2eTest/Entities/DatabaseInfo.cs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace E2eTest.Entities;
8+
internal sealed class DatabaseInfo
9+
{
10+
public string? Name { get; set; }
11+
public int Version { get; set; }
12+
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using E2eTest.Entities;
2+
using Microsoft.AspNetCore.Mvc.RazorPages;
3+
using Microsoft.Playwright;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Diagnostics.CodeAnalysis;
7+
using System.Linq;
8+
using System.Text;
9+
using System.Text.Json;
10+
using System.Threading.Tasks;
11+
using System.Xml.Linq;
12+
13+
namespace E2eTest.Extensions;
14+
internal static class AssertExtensions
15+
{
16+
private class JsonElementDeepComparer : IEqualityComparer<JsonElement>
17+
{
18+
public static JsonElementDeepComparer Instance { get; } = new();
19+
public bool Equals(JsonElement x, JsonElement y)
20+
{
21+
return JsonElement.DeepEquals(x, y);
22+
}
23+
public int GetHashCode([DisallowNull] JsonElement obj)
24+
{
25+
throw new NotImplementedException();
26+
}
27+
}
28+
29+
public static void AreJsonEqual(this Assert _, string expected, string actual)
30+
{
31+
var element1 = JsonSerializer.Deserialize<JsonElement>(expected);
32+
var element2 = JsonSerializer.Deserialize<JsonElement>(actual);
33+
Assert.AreEqual(element1, element2, JsonElementDeepComparer.Instance);
34+
}
35+
}

E2eTest/Extensions/PageExtensions.cs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using E2eTest.Entities;
2+
using Microsoft.AspNetCore.Mvc.RazorPages;
3+
using Microsoft.Playwright;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using System.Xml.Linq;
10+
11+
namespace E2eTest.Extensions;
12+
internal static class PageExtensions
13+
{
14+
public static async ValueTask DeleteDatabaseAsync(this IPage page, string database)
15+
{
16+
_ = await page.EvaluateAsync($"indexedDB.deleteDatabase('{database}')");
17+
var databases = await page.EvaluateAsync<DatabaseInfo[]>("indexedDB.databases()");
18+
Assert.IsFalse(databases!.Any(x => x.Name == database));
19+
}
20+
}

E2eTest/MSTestSettings.cs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]

E2eTest/OpenTest.cs

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using E2eTest.Entities;
2+
using E2eTest.Extensions;
3+
using E2eTestWebApp.TestPages;
4+
5+
namespace E2eTest;
6+
7+
[TestClass]
8+
public class OpenTest : TestBase<OpenTestPage>
9+
{
10+
[TestMethod]
11+
public async Task DirectOpenTest()
12+
{
13+
var page = await this.NewPageAsync();
14+
await page.DeleteDatabaseAsync("OpenTest.DirectOpen");
15+
16+
var result = await this.RunTestPageMethodAsync(p => p.DirectOpen);
17+
Assert.AreEqual("OK", result);
18+
19+
var databases = await page.EvaluateAsync<DatabaseInfo[]>("indexedDB.databases()");
20+
// The actual version will be 10:
21+
// https://dexie.org/docs/Dexie/Dexie.version()
22+
Assert.IsTrue(databases!.Any(x => x.Name == "OpenTest.DirectOpen" && x.Version == 10));
23+
}
24+
25+
[TestMethod]
26+
public async Task RegisteredOpenTest1()
27+
{
28+
var page = await this.NewPageAsync();
29+
await page.DeleteDatabaseAsync("OpenTest.RegisteredOpen1");
30+
31+
var result = await this.RunTestPageMethodAsync(p => p.RegisteredOpen1);
32+
Assert.AreEqual("OK", result);
33+
34+
var databases = await page.EvaluateAsync<DatabaseInfo[]>("indexedDB.databases()");
35+
// The actual version will be 20:
36+
// https://dexie.org/docs/Dexie/Dexie.version()
37+
Assert.IsTrue(databases!.Any(x => x.Name == "OpenTest.RegisteredOpen1" && x.Version == 20));
38+
}
39+
40+
[TestMethod]
41+
public async Task RegisteredOpenTest2()
42+
{
43+
var page = await this.NewPageAsync();
44+
await page.DeleteDatabaseAsync("OpenTest.RegisteredOpen2");
45+
46+
var result = await this.RunTestPageMethodAsync(p => p.RegisteredOpen2);
47+
Assert.AreEqual("OK", result);
48+
49+
var databases = await page.EvaluateAsync<DatabaseInfo[]>("indexedDB.databases()");
50+
// The actual version will be 30:
51+
// https://dexie.org/docs/Dexie/Dexie.version()
52+
Assert.IsTrue(databases!.Any(x => x.Name == "OpenTest.RegisteredOpen2" && x.Version == 30));
53+
}
54+
55+
[TestMethod]
56+
public async Task GetTest()
57+
{
58+
var page = await this.NewPageAsync();
59+
await page.DeleteDatabaseAsync("OpenTest.Get");
60+
61+
var result = await this.RunTestPageMethodAsync(p => p.Get);
62+
Assert.AreEqual("OpenTest.Get", result);
63+
}
64+
65+
[TestMethod]
66+
public async Task StoreSchemaTest()
67+
{
68+
var page = await this.NewPageAsync();
69+
await page.GotoAsync("/");
70+
71+
await page.DeleteDatabaseAsync("OpenTest.StoreSchema");
72+
73+
var result = await this.RunTestPageMethodAsync(p => p.StoreSchema);
74+
Assert.AreEqual("OK", result);
75+
76+
var storeSchema = await page.EvaluateAsync<string>("storeSchema('OpenTest.StoreSchema')");
77+
Assert.That.AreJsonEqual("""
78+
{
79+
"S1": {
80+
"autoIncrement": true, "keyPath": ["P11"],
81+
"indexes": {
82+
"I11": {"keyPath": ["I11"],"multiEntry": false,"unique": false},
83+
"I12": {"keyPath": ["I12"],"multiEntry": false,"unique": false},
84+
"U11": {"keyPath": ["U11"],"multiEntry": false,"unique": true},
85+
"U12": {"keyPath": ["U12"],"multiEntry": false,"unique": true}
86+
}
87+
},
88+
"S2": {
89+
"autoIncrement": false, "keyPath": ["P21"],
90+
"indexes": {
91+
"U21": {"keyPath": ["U21"],"multiEntry": false,"unique": true}
92+
}
93+
}
94+
}
95+
""", storeSchema);
96+
}
97+
}

E2eTest/OpenTest.cs.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
window.storeSchema = (database) =>
2+
{
3+
return new Promise((resolve, reject) =>
4+
{
5+
const request = window.indexedDB.open(database);
6+
request.onsuccess = () =>
7+
{
8+
const asArray = (value) =>
9+
{
10+
if (value === null || value === undefined)
11+
return [];
12+
else if (Array.isArray(value))
13+
return value;
14+
return [value];
15+
};
16+
17+
const db = request.result;
18+
const result = {};
19+
const transaction = db.transaction(db.objectStoreNames);
20+
for (const storeName of db.objectStoreNames)
21+
{
22+
const store = transaction.objectStore(storeName);
23+
24+
const indexes = {};
25+
for (const indexName of store.indexNames)
26+
{
27+
const index = store.index(indexName)
28+
indexes[index.name] = {
29+
keyPath: asArray(index.keyPath),
30+
multiEntry: index.multiEntry,
31+
unique: index.unique
32+
}
33+
}
34+
35+
result[store.name] = {
36+
autoIncrement: store.autoIncrement,
37+
keyPath: asArray(store.keyPath),
38+
indexes: indexes
39+
};
40+
}
41+
resolve(JSON.stringify(result));
42+
};
43+
request.onerror = () => reject("Failed to open database.");
44+
request.onblocked = () => reject("Failed to open database.");
45+
})
46+
};

E2eTest/Program.cs

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using System.Diagnostics;
2+
3+
namespace E2eTest;
4+
5+
[TestClass]
6+
public static class Program
7+
{
8+
private static Process? server = null;
9+
public static string BaseUrl { get; private set; } = "";
10+
11+
// https://stackoverflow.com/questions/4029886/
12+
private static int count = 0;
13+
14+
[AssemblyInitialize]
15+
public static async Task InitializeAsync(TestContext context)
16+
{
17+
count++;
18+
if (count is not 1)
19+
return;
20+
21+
using var currentProcess = Process.GetCurrentProcess();
22+
var dotnetRunArguments = "--no-build --project ../../../../E2eTestWebApp";
23+
var webAppArguments = $"--E2eTest {currentProcess.Id}";
24+
var server = new Process
25+
{
26+
StartInfo = new ProcessStartInfo()
27+
{
28+
FileName = "dotnet",
29+
Arguments = $"run {dotnetRunArguments} -- {webAppArguments}",
30+
UseShellExecute = false,
31+
RedirectStandardOutput = true,
32+
RedirectStandardError = true
33+
}
34+
};
35+
36+
try
37+
{
38+
if (!server.Start())
39+
throw new Exception("Failed to start E2eTestWebApp. Process.Start returns false.");
40+
}
41+
catch
42+
{
43+
server.Dispose();
44+
throw;
45+
}
46+
47+
try
48+
{
49+
var lines = new List<string>();
50+
for (; ; )
51+
{
52+
var line = await server.StandardOutput.ReadLineAsync();
53+
if (line is null)
54+
{
55+
throw new Exception(
56+
$"Failed to start E2eTestWebApp. The output stream ended accidentally.{Environment.NewLine}" +
57+
$"The previous message is:{Environment.NewLine}" +
58+
string.Join(Environment.NewLine, lines));
59+
}
60+
61+
lines.Add(line);
62+
line = line.TrimStart();
63+
if (line.StartsWith("Now listening on: http://"))
64+
{
65+
BaseUrl = line.Substring("Now listening on: ".Length).TrimEnd();
66+
Program.server = server;
67+
return;
68+
}
69+
}
70+
}
71+
catch
72+
{
73+
if (!server.HasExited)
74+
server.Kill(true);
75+
server.Dispose();
76+
throw;
77+
}
78+
}
79+
80+
[AssemblyCleanup]
81+
public static void Cleanup()
82+
{
83+
count--;
84+
if (count is not 0)
85+
return;
86+
87+
if (server is not null)
88+
{
89+
if (!server.HasExited)
90+
server.Kill(true);
91+
server.Dispose();
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)