- Separado el control de poligonos excluyentes
- Añadido interpreacion de tipologia de suelo en segmentos andando - Persistencia modo andando - Transbordo penalizado a 6 - Añadidos varias zonas de prohibicion de poligonos
This commit is contained in:
111
tests/RutasDBUS.RegressionTests/Program.cs
Normal file
111
tests/RutasDBUS.RegressionTests/Program.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using RutasDBUS.Components.Pages;
|
||||
using RutasDBUS.Modelos.Historial;
|
||||
using RutasDBUS.Modelos.Planificacion;
|
||||
using RutasDBUS.Servicios.Caminata;
|
||||
using RutasDBUS.Servicios.Configuracion;
|
||||
using RutasDBUS.Servicios.Planificacion;
|
||||
|
||||
static void Check(bool result, string message)
|
||||
{
|
||||
if (!result) throw new Exception(message);
|
||||
Console.WriteLine("OK " + message);
|
||||
}
|
||||
|
||||
var baseMethod = typeof(PlanificadorRutas).GetMethod("CrearConfiguracionBaseHistorial", BindingFlags.Static | BindingFlags.NonPublic)!;
|
||||
var defaults = (ConfiguracionHistorialRuta)baseMethod.Invoke(null, null)!;
|
||||
Check(defaults.PesoTransbordoRankingMinutos == 6, "Penalizacion inicial de 6 minutos");
|
||||
|
||||
var folder = Path.Combine(Path.GetTempPath(), "rutasdbus-regression-" + Guid.NewGuid().ToString("N"));
|
||||
IConfiguration Config() => new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["ValoresFabrica:Directorio"] = folder
|
||||
}).Build();
|
||||
ServicioValoresFabrica Service() => new(Config(), NullLogger<ServicioValoresFabrica>.Instance);
|
||||
|
||||
try
|
||||
{
|
||||
defaults.PesoTransbordoRankingMinutos = 9;
|
||||
defaults.FechaSimulada = null;
|
||||
defaults.HoraSimulada = null;
|
||||
Directory.CreateDirectory(folder);
|
||||
var file = Path.Combine(folder, "configuracion-fabrica.json");
|
||||
await File.WriteAllTextAsync(file, JsonSerializer.Serialize(defaults));
|
||||
var loaded = await Service().CargarAsync();
|
||||
Check(loaded?.PesoTransbordoRankingMinutos == 9, "Valores compartidos entre instancias");
|
||||
Check(loaded is { FechaSimulada: null, HoraSimulada: null }, "Los valores de fabrica no fijan la fecha y hora");
|
||||
File.WriteAllText(file, "{incompleto");
|
||||
Check(await Service().CargarAsync() is null, "JSON danado permite usar valores base");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Solo datos temporales creados por esta prueba.
|
||||
if (Directory.Exists(folder)) Directory.Delete(folder, recursive: true);
|
||||
}
|
||||
|
||||
var fake = new FakeValhalla();
|
||||
var planner = new PlanificadorRutasServicio(null!, null!, fake, null!, null!, null!, null!);
|
||||
var walk = typeof(PlanificadorRutasServicio).GetMethod("WalkValhallaCached", BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||||
async Task<(double[,]? linea, double distancia, double duracion)> Walk(List<List<double[]>>? zones = null) =>
|
||||
await (Task<(double[,]?, double, double)>)walk.Invoke(planner, new object?[] { 43.31, -1.99, 43.32, -1.98, zones })!;
|
||||
List<double[]> Ring(double lon, double lat, double size) =>
|
||||
new() { new[] { lon, lat }, new[] { lon + size, lat }, new[] { lon + size, lat + size },
|
||||
new[] { lon, lat + size }, new[] { lon, lat } };
|
||||
|
||||
await Walk();
|
||||
var far = new List<List<double[]>> { Ring(-2.3, 43.0, .01) };
|
||||
await Walk(far);
|
||||
Check(fake.Calls == 1, "Zona lejana reutiliza la geometria conocida");
|
||||
var near = new List<List<double[]>> { Ring(-1.985, 43.315, .001), Ring(-1.982, 43.317, .001) };
|
||||
await Walk(near);
|
||||
Check(fake.Zones == 2, "Se envian todos los poligonos");
|
||||
await Walk(near);
|
||||
Check(fake.Calls == 2, "Mismas zonas reutilizan cache restringida");
|
||||
near[0][0][0] += .00001;
|
||||
await Walk(near);
|
||||
Check(fake.Calls == 3, "Mover un vertice invalida esa entrada de cache");
|
||||
await Walk(new() { Ring(-2.2, 43.1, .5) });
|
||||
Check(fake.Calls == 4, "Una zona que rodea la ruta no se ignora aunque sus vertices esten lejos");
|
||||
fake.Fail = true;
|
||||
var retry = new List<List<double[]>> { Ring(-1.984, 43.312, .0002) };
|
||||
await Walk(retry);
|
||||
fake.Fail = false;
|
||||
await Walk(retry);
|
||||
Check(fake.Calls == 6, "Un fallo temporal no queda guardado en cache de zonas");
|
||||
var page = new PlanificadorRutas();
|
||||
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
var options = (List<AlternativaRuta>)typeof(PlanificadorRutas).GetField("_opcionesRuta", flags)!.GetValue(page)!;
|
||||
var selected = typeof(PlanificadorRutas).GetField("_indiceAlternativaSeleccionada", flags)!;
|
||||
var remember = typeof(PlanificadorRutas).GetMethod("ObtenerProveedorSoloAPieParaRecalculo", flags)!;
|
||||
string? Remember(bool keep) => (string?)remember.Invoke(page, new object[] { keep });
|
||||
options.Add(new() { EsSoloAPie = true, ProveedorRuta = "v1" });
|
||||
options.Add(new() { EsSoloAPie = true, ProveedorRuta = "v2" });
|
||||
selected.SetValue(page, 1);
|
||||
Check(Remember(true) == "v2", "Se conserva la version andando seleccionada");
|
||||
options.Clear();
|
||||
Check(Remember(true) == "v2", "Otro cambio de punto durante el calculo conserva v2");
|
||||
options.Add(new() { EsSoloAPie = false });
|
||||
selected.SetValue(page, 0);
|
||||
Check(Remember(true) is null, "Seleccionar bus libera la preferencia andando");
|
||||
options.Clear();
|
||||
Check(Remember(false) is null, "Nueva seleccion de criterio no hereda la preferencia andando");
|
||||
Console.WriteLine("Pruebas completadas.");
|
||||
|
||||
sealed class FakeValhalla : IClienteValhalla
|
||||
{
|
||||
public int Calls { get; private set; }
|
||||
public int Zones { get; private set; }
|
||||
public bool Fail { get; set; }
|
||||
public Task<(double[,]? Linea, double DistanciaMetros, double DuracionSegundos)> ObtenerRutaAsync(
|
||||
double latitudDesde, double longitudDesde, double latitudHasta, double longitudHasta,
|
||||
string costing = "pedestrian", List<List<double[]>>? excludePolygons = null)
|
||||
{
|
||||
Calls++;
|
||||
Zones = excludePolygons?.Count ?? 0;
|
||||
return Task.FromResult<(double[,]?, double, double)>(Fail ? (null, 0, 0) :
|
||||
(new[,] { { latitudDesde, longitudDesde }, { latitudHasta, longitudHasta } }, 100, 100));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../RutasDBUS/RutasDBUS.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
80
tests/verify-zones.cjs
Normal file
80
tests/verify-zones.cjs
Normal file
@@ -0,0 +1,80 @@
|
||||
const { chromium } = require(process.argv[2] || "playwright");
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ channel: "msedge", headless: true });
|
||||
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
|
||||
const errors = [];
|
||||
page.on("pageerror", error => errors.push(error.stack || error.message));
|
||||
const artifacts = path.join(__dirname, "../artifacts/zones-check");
|
||||
fs.mkdirSync(artifacts, { recursive: true });
|
||||
try {
|
||||
await page.goto("http://127.0.0.1:5199", { waitUntil: "domcontentloaded" });
|
||||
await page.waitForFunction(() => !!window.rt?._dotNetHelper && !!window.__rtLeafletMap);
|
||||
const initialErrors = errors.slice();
|
||||
await page.getByRole("button", { name: "Abrir zonas a evitar", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Añadir zona", exact: true }).click();
|
||||
for (const [x, y] of [[600, 420], [850, 430], [760, 640]]) {
|
||||
await page.mouse.click(x, y);
|
||||
await page.waitForTimeout(250);
|
||||
}
|
||||
await page.getByRole("button", { name: "Cerrar zona", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Añadir zona", exact: true }).waitFor();
|
||||
assert.equal(await page.locator(".zones-panel__item").count(), 1);
|
||||
assert.equal(await page.locator(".rt-exclusion-vertex").count(), 3);
|
||||
const polygon = page.locator('path[stroke="#e879f9"]');
|
||||
const previousShape = await polygon.getAttribute("d");
|
||||
const marker = await page.locator(".rt-exclusion-vertex").first().boundingBox();
|
||||
await page.mouse.move(marker.x + marker.width / 2, marker.y + marker.height / 2);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(marker.x - 55, marker.y + 65, { steps: 10 });
|
||||
assert.notEqual(await polygon.getAttribute("d"), previousShape, "Poligono se mueve durante el arrastre");
|
||||
await page.mouse.up();
|
||||
await page.waitForTimeout(250);
|
||||
await page.getByRole("button", { name: "Añadir zona", exact: true }).click();
|
||||
for (const [x, y] of [[920, 460], [1090, 460], [1020, 640]]) {
|
||||
await page.mouse.click(x, y);
|
||||
await page.waitForTimeout(250);
|
||||
}
|
||||
await page.getByRole("button", { name: "Cerrar zona", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Añadir zona", exact: true }).waitFor();
|
||||
assert.equal(await page.locator(".zones-panel__item").count(), 2);
|
||||
await page.screenshot({ path: path.join(artifacts, "desktop.png") });
|
||||
const heights = await page.locator(".zones-panel__actions .btn-ghost").evaluateAll(
|
||||
buttons => buttons.map(button => button.getBoundingClientRect().height));
|
||||
assert.equal(new Set(heights).size, 1, "Botones de igual altura");
|
||||
await page.getByRole("button", { name: "Cerrar herramienta", exact: true }).click();
|
||||
await page.locator(".zones-panel").waitFor({ state: "detached" });
|
||||
assert.equal(await page.locator(".rt-exclusion-vertex").count(), 0);
|
||||
assert.equal(await page.locator('path[stroke="#e879f9"], path[stroke="#f97316"]').count(), 0);
|
||||
await page.getByRole("button", { name: "Abrir zonas a evitar", exact: true }).click();
|
||||
await page.locator(".zones-panel").waitFor();
|
||||
assert.equal(await page.locator(".zones-panel__item").count(), 2, "Reabrir conserva zonas editables");
|
||||
await page.getByRole("button", { name: "Eliminar zona", exact: true }).first().click();
|
||||
await page.waitForFunction(() => document.querySelectorAll(".zones-panel__item").length === 1);
|
||||
assert.equal(await page.locator(".zones-panel__item").count(), 1);
|
||||
await page.getByRole("button", { name: "Borrar todas", exact: true }).click();
|
||||
await page.waitForFunction(() => document.querySelectorAll(".zones-panel__item").length === 0);
|
||||
assert.equal(await page.locator(".zones-panel__item").count(), 0);
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.screenshot({ path: path.join(artifacts, "mobile.png") });
|
||||
const box = await page.locator(".zones-panel").boundingBox();
|
||||
assert.ok(box.x >= 0 && box.x + box.width <= 390 && box.y + box.height <= 844, "Panel dentro del movil");
|
||||
await page.getByRole("button", { name: "Añadir zona", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Cerrar herramienta", exact: true }).click();
|
||||
await page.setViewportSize({ width: 1440, height: 1000 });
|
||||
await page.getByTitle("Configuración", { exact: true }).click();
|
||||
assert.equal(await page.locator(".config-more-menu").count(), 0, "No hay opcion para guardar valores de fabrica");
|
||||
const externalErrors = errors.filter(error =>
|
||||
error.includes("esri-leaflet-geocoder@3.1.4/dist/esri-leaflet-geocoder.js") &&
|
||||
error.includes("reading 'extend'"));
|
||||
const newErrors = errors.filter(error => !initialErrors.includes(error) && !externalErrors.includes(error));
|
||||
assert.equal(newErrors.length, 0, newErrors.join("\n"));
|
||||
if (externalErrors.length) console.log("Aviso del complemento externo de geocodificacion: " + externalErrors[0]);
|
||||
console.log("OK multiples zonas, arrastre en vivo, cierre, reapertura, borrado, alturas de botones y movil");
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
})().catch(error => { console.error(error); process.exitCode = 1; });
|
||||
Reference in New Issue
Block a user