ElyTable: zoom and DPI keep the scroll in print twips; drag and zoom tests far down a sheet (R2.16)
This commit is contained in:
20 files changed
+155
-64
No files matched your search
@@ -485,29 +485,3 @@ fn enter_and_tab_move_the_cell_cursor() {
|
||||
});
|
||||
assert_eq!(right.cell.address, "G2", "Tab moves right");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_click_far_down_selects_the_cell_under_it() {
|
||||
if skip() {
|
||||
return;
|
||||
}
|
||||
let (_dir, mut worker, doc, seen) = open();
|
||||
// One painted tile fixes the kernel's scale.
|
||||
let (side, twips) = (512u32, 3840);
|
||||
let ppt = f64::from(side) / f64::from(twips);
|
||||
doc.paint_tile(&mut worker, (0, 0, twips, twips), (side, side)).expect("a tile");
|
||||
let geometry = sheet::geometry(&worker.values(&doc, sheet::GEOMETRY).expect("geometry")).expect("parsed");
|
||||
worker.run(&doc, "cell.goto", &["B4998"]).expect("B4998");
|
||||
let at = poll_until(&mut worker, &doc, &seen, SETTLE, |seen| seen.cell.address == "B4998");
|
||||
let (x, y, w, h) = at.cell.cursor.expect("the cursor");
|
||||
worker.run(&doc, "cell.goto", &["A1"]).expect("A1");
|
||||
let home = poll_until(&mut worker, &doc, &at, SETTLE, |seen| seen.cell.address == "A1");
|
||||
// The cursor is in print twips; clicks take tile twips.
|
||||
let (x, y) = (geometry.columns.tile(x + w / 2, ppt), geometry.rows.tile(y + h / 2, ppt));
|
||||
for kind in [0, 1] {
|
||||
let click = crate::Mouse { kind, x, y, count: 1, buttons: 1, modifiers: 0 };
|
||||
worker.post_mouse(&doc, click).expect("clicks");
|
||||
}
|
||||
let after = poll_until(&mut worker, &doc, &home, SETTLE, |seen| seen.cell.address != "A1");
|
||||
assert_eq!(after.cell.address, "B4998");
|
||||
}
|
||||
@@ -40,7 +40,7 @@ impl Render for LokView {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let dpi = window.scale_factor();
|
||||
if dpi != self.geom.dpi {
|
||||
self.geom.dpi = dpi;
|
||||
self.keep_scroll(|view| view.geom.dpi = dpi);
|
||||
self.retire_tiles();
|
||||
}
|
||||
self.follow_theme(cx.theme().is_dark());
|
||||
@@ -210,7 +210,7 @@ impl LokView {
|
||||
let down = self.viewport.1 * TWIPS_PER_PIXEL / h as f32;
|
||||
let zoom = (across.min(down) * MARGIN).min(4.0);
|
||||
if (zoom - self.geom.zoom).abs() > 0.001 {
|
||||
self.geom.zoom = zoom;
|
||||
self.keep_scroll(|view| view.geom.zoom = zoom);
|
||||
self.retire_tiles();
|
||||
self.failed.clear();
|
||||
}
|
||||
|
||||
@@ -49,11 +49,9 @@ impl LokView {
|
||||
self.geom.twips_of(self.viewport.0) - fx,
|
||||
self.geom.twips_of(self.viewport.1) - fy,
|
||||
);
|
||||
// Room past the target, so the clamp lets it show.
|
||||
self.reach = (
|
||||
self.reach.0.max(x + w + vw + fx),
|
||||
self.reach.1.max(y + h + vh + fy),
|
||||
);
|
||||
// Room past the target, in print twips for later zooms.
|
||||
let need = self.print_of((x + w + vw + fx, y + h + vh + fy));
|
||||
self.reach = (self.reach.0.max(need.0), self.reach.1.max(need.1));
|
||||
if x >= fx && (x < fx + self.scroll.0 || x + w > fx + self.scroll.0 + vw) {
|
||||
self.scroll.0 = x - fx - vw / 2;
|
||||
}
|
||||
@@ -104,10 +102,10 @@ impl LokView {
|
||||
if self.fit {
|
||||
return self.doc_size;
|
||||
}
|
||||
(
|
||||
self.tile_of((
|
||||
self.doc_size.0.max(self.reach.0),
|
||||
self.doc_size.1.max(self.reach.1),
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
/// The print-twips document point under a view position (LOK-04).
|
||||
|
||||
@@ -48,6 +48,14 @@ impl LokView {
|
||||
self.tile_of(self.frozen)
|
||||
}
|
||||
|
||||
/// Rescales, keeping the scrolling pane's start in place.
|
||||
pub(super) fn keep_scroll(&mut self, change: impl FnOnce(&mut Self)) {
|
||||
let start = self.scrolled_start();
|
||||
change(self);
|
||||
let ((x, y), (fx, fy)) = (self.tile_of(start), self.frozen_tiles());
|
||||
self.scroll = (x - fx, y - fy);
|
||||
}
|
||||
|
||||
/// Where the scrolling pane starts, in print twips.
|
||||
pub fn scrolled_start(&self) -> (i32, i32) {
|
||||
let (fx, fy) = self.frozen_tiles();
|
||||
|
||||
@@ -73,6 +73,44 @@ const SHORT_ROWS: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
|
||||
</table:table></office:spreadsheet></office:body>
|
||||
</office:document>"#;
|
||||
|
||||
/// A short-row sheet at 2× DPI, scrolled to `cell`'s row.
|
||||
fn far_down(cx: &mut gpui::TestAppContext, install: &std::path::Path) -> (Entity<LokView>, crate::sheet::Geometry) {
|
||||
let (view, worker) = cx.update(|app| live_view_of(app, install, "short.fods", SHORT_ROWS));
|
||||
let json = cx.update(|app| {
|
||||
let read = view.read(app);
|
||||
worker.lock().expect("the worker lock").values(&read.doc, crate::sheet::GEOMETRY).expect("geometry")
|
||||
});
|
||||
let geometry = crate::sheet::geometry(&json).expect("parsed");
|
||||
let row = cell(&geometry, 1, 4997);
|
||||
cx.update(|app| {
|
||||
view.update(app, |view, cx| {
|
||||
view.geom.dpi = 2.0;
|
||||
view.set_geometry(geometry.clone(), cx);
|
||||
view.prepare(Bounds { origin: point(px(0.0), px(0.0)), size: size(px(800.0), px(600.0)) });
|
||||
view.show(row, cx);
|
||||
})
|
||||
});
|
||||
(view, geometry)
|
||||
}
|
||||
|
||||
/// Column `x`, row `y` in print twips.
|
||||
fn cell(geometry: &crate::sheet::Geometry, x: u32, y: u32) -> crate::Rect {
|
||||
(geometry.columns.start(x), geometry.rows.start(y), geometry.columns.size(x), geometry.rows.size(y))
|
||||
}
|
||||
|
||||
/// Waits until the kernel reports `want` as the selection.
|
||||
fn selected(view: &Entity<LokView>, cx: &mut gpui::TestAppContext, want: &str) -> String {
|
||||
let mut address = String::new();
|
||||
for _ in 0..200 {
|
||||
cx.executor().tick();
|
||||
address = cx.update(|app| view.read(app).cell().address.clone());
|
||||
if address == want {
|
||||
break;
|
||||
}
|
||||
}
|
||||
address
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_click_far_down_a_sheet_selects_the_cell_under_it() {
|
||||
let Some(install) = crate::client::tests::install() else {
|
||||
@@ -80,38 +118,59 @@ fn a_click_far_down_a_sheet_selects_the_cell_under_it() {
|
||||
return;
|
||||
};
|
||||
let mut cx = gpui::TestAppContext::single();
|
||||
let (view, worker) = cx.update(|app| live_view_of(app, &install, "short.fods", SHORT_ROWS));
|
||||
let json = cx.update(|app| {
|
||||
let read = view.read(app);
|
||||
worker.lock().expect("the worker lock").values(&read.doc, crate::sheet::GEOMETRY).expect("geometry")
|
||||
});
|
||||
let geometry = crate::sheet::geometry(&json).expect("parsed");
|
||||
let (row, column) = (4997, 1);
|
||||
let cell = (
|
||||
geometry.columns.start(column),
|
||||
geometry.rows.start(row),
|
||||
geometry.columns.size(column),
|
||||
geometry.rows.size(row),
|
||||
);
|
||||
let (view, geometry) = far_down(&mut cx, &install);
|
||||
let (x, y, w, h) = cell(&geometry, 1, 4997);
|
||||
cx.update(|app| {
|
||||
view.update(app, |view, cx| {
|
||||
view.geom.dpi = 2.0;
|
||||
view.set_geometry(geometry.clone(), cx);
|
||||
view.prepare(Bounds { origin: point(px(0.0), px(0.0)), size: size(px(800.0), px(600.0)) });
|
||||
view.show(cell, cx);
|
||||
let at = view.window_point((cell.0 + cell.2 / 2, cell.1 + cell.3 / 2));
|
||||
view.update(app, |view, _| {
|
||||
let at = view.window_point((x + w / 2, y + h / 2));
|
||||
for kind in [0, 1] {
|
||||
view.mouse(kind, at, 1, 1, &gpui::Modifiers::default());
|
||||
}
|
||||
})
|
||||
});
|
||||
let mut address = String::new();
|
||||
for _ in 0..200 {
|
||||
cx.executor().tick();
|
||||
address = cx.update(|app| view.read(app).cell().address.clone());
|
||||
if address == "B4998" {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert_eq!(address, "B4998");
|
||||
assert_eq!(selected(&view, &mut cx, "B4998"), "B4998");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_drag_far_down_a_sheet_selects_the_range_under_it() {
|
||||
let Some(install) = crate::client::tests::install() else {
|
||||
eprintln!("SKIP: no pinned LOK build");
|
||||
return;
|
||||
};
|
||||
let mut cx = gpui::TestAppContext::single();
|
||||
let (view, geometry) = far_down(&mut cx, &install);
|
||||
let middle = |(x, y, w, h): crate::Rect| (x + w / 2, y + h / 2);
|
||||
let (from, to) = (middle(cell(&geometry, 1, 4997)), middle(cell(&geometry, 3, 5000)));
|
||||
cx.update(|app| {
|
||||
view.update(app, |view, _| {
|
||||
let modifiers = gpui::Modifiers::default();
|
||||
view.mouse(0, view.window_point(from), 1, 1, &modifiers);
|
||||
view.mouse(2, view.window_point(((from.0 + to.0) / 2, (from.1 + to.1) / 2)), 1, 1, &modifiers);
|
||||
view.mouse(2, view.window_point(to), 1, 1, &modifiers);
|
||||
view.mouse(1, view.window_point(to), 1, 1, &modifiers);
|
||||
})
|
||||
});
|
||||
assert_eq!(selected(&view, &mut cx, "B4998:D5001"), "B4998:D5001");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zooming_far_down_a_sheet_keeps_the_rows_in_view() {
|
||||
let Some(install) = crate::client::tests::install() else {
|
||||
eprintln!("SKIP: no pinned LOK build");
|
||||
return;
|
||||
};
|
||||
let mut cx = gpui::TestAppContext::single();
|
||||
let (view, geometry) = far_down(&mut cx, &install);
|
||||
let middle = |cx: &mut gpui::TestAppContext| {
|
||||
cx.update(|app| {
|
||||
let read = view.read(app);
|
||||
let (w, h) = read.viewport();
|
||||
let at = read.origin() + point(px(w / 2.0), px(h / 2.0));
|
||||
geometry.rows.at(read.doc_point(at).1)
|
||||
})
|
||||
};
|
||||
let before = middle(&mut cx);
|
||||
cx.update(|app| view.update(app, |view, cx| view.set_zoom(0.8, cx)));
|
||||
let after = middle(&mut cx);
|
||||
assert!(before.abs_diff(after) <= 1, "row {before} became {after}");
|
||||
}
|
||||
@@ -47,13 +47,15 @@ impl LokView {
|
||||
Some(at) => (f32::from(at.x - self.origin.x), f32::from(at.y - self.origin.y)),
|
||||
None => (self.viewport.0 / 2.0, self.viewport.1 / 2.0),
|
||||
};
|
||||
let under = (self.scroll.0 + self.geom.twips_of(ax), self.scroll.1 + self.geom.twips_of(ay));
|
||||
// Anchored in print twips: tile twips change with the zoom.
|
||||
let under = self.print_of((self.scroll.0 + self.geom.twips_of(ax), self.scroll.1 + self.geom.twips_of(ay)));
|
||||
self.fit = false;
|
||||
if (zoom - self.geom.zoom).abs() > 0.0001 {
|
||||
self.geom.zoom = zoom;
|
||||
self.retire_tiles();
|
||||
self.failed.clear();
|
||||
}
|
||||
let under = self.tile_of(under);
|
||||
self.scroll = (under.0 - self.geom.twips_of(ax), under.1 - self.geom.twips_of(ay));
|
||||
self.clamp_scroll();
|
||||
log::debug!("lok: zoom {:.0}%", zoom * 100.0);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
site/e2e/night.spec.ts:69:对比度计算只取颜色的前三个分量,丢掉 alpha,随后直接与页面背景比较,因此低透明度链接能通过 4.5:1 断言。复现:在临时副本中将浅色链接设为 `rgba(42, 106, 208, 0.1)`、深色链接设为 `rgba(120, 166, 238, 0.1)`;运行 `pnpm e2e e2e/night.spec.ts --grep '4.5'`,两项均通过。浏览器读取下载链接的实际颜色并与背景合成后,对比度分别为 1.1425:1、1.1874:1。修复方向:按实际背景合成颜色及有效透明度后计算对比度,覆盖低透明度链接的回归场景。证据:`/var/folders/g4/sj7njkr93jz0rgmg0b_zqnvr0000gn/T/ely-r21-evidence-7s8j6kd8/mutation-alpha.log`。
|
||||
|
||||
site/e2e/text.spec.ts:27:语言检查只遍历仍存在的 `[data-t]` 元素,并从生产代码的 `TEXT` 取得预期值;缺少 R2.1 打开步骤的存在性与内容断言。复现:在临时副本中从 `main.ts` 的 footer 移除 `said("p", "unsigned")`,页面中英两种语言均失去打开步骤,`pnpm e2e e2e/text.spec.ts` 的 7 项仍全部通过。修复方向:在中文首次加载、英文首次加载和语言切换后,明确断言下载区说明可见,并独立核对“系统设置 / System Settings”“隐私与安全性 / Privacy & Security”“仍要打开 / Open Anyway”。证据:`/var/folders/g4/sj7njkr93jz0rgmg0b_zqnvr0000gn/T/ely-r21-evidence-7s8j6kd8/mutation-unsigned.log`。原始实现已独立通过构建、13 项单元测试、34 项端到端测试及注释检查;临时源码副本与构建产物已删除,原始源码哈希核对通过。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
site/e2e/night.spec.ts:82:对比度断言只合成文字颜色的 alpha,遗漏元素及祖先的 CSS opacity,上轮要求的有效透明度校验仍有缺口。具体复现:在临时副本中添加 `#download a { opacity: 0.1; }`,运行 `pnpm e2e e2e/night.spec.ts --grep '4.5'`,浅色与深色两项断言均通过;独立读取源码下载链接的颜色、背景及有效透明度,合成后的实际对比度分别为 1.1425:1、1.1874:1,违反 R2.1 与 PRD §20.3 的 4.5:1 回归要求。修复方向:计算文字的有效透明度时纳入元素与祖先的 opacity,并用该低透明度场景验证两种主题的断言能捕获退化。证据:`/var/folders/g4/sj7njkr93jz0rgmg0b_zqnvr0000gn/T/ely-r21-r2-fh521a5u/mutation-opacity.log`。上轮原始 alpha 变异已使两项对比度断言失败,删除打开步骤已使新增存在性断言失败;原始代码独立通过构建、13 项单元测试、35 项端到端测试(含外部下载链接检查)及注释检查。临时源码副本和构建目录已删除,原始源码哈希核对通过。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,9 @@
|
||||
FAIL
|
||||
|
||||
scripts/inert-lo.sh:14 — 注销操作缺少幂等性,阻断后续构建。复现:对真实构建包的副本先执行 `lsregister -f`,再连续执行两次 `scripts/inert-lo.sh`;第一次退出 0,第二次退出 1,报 `failed to scan ...: -10814`。尚未登记的新副本同样退出 1。`scripts/build-libreoffice.sh:87` 每次构建都调用该脚本,已注销状态因此使正常重建失败。修复方向:明确识别并接受登记记录已经不存在的状态,继续暴露其他注销错误,覆盖首次执行与重复执行。
|
||||
|
||||
tests/scripts/lo-unregistered.sh:23 — `|| true` 吞掉 Swift 查询失败,并将空输出当作验收成功。复现:在 PATH 前置一个向 stderr 输出错误并退出 42 的 `swift`,运行该检查仍退出 0,打印 `ok: no LibreOffice in Open With`,整个 NSWorkspace 查询均未执行。修复方向:单独检查 Swift 的退出状态,成功取得查询结果后再筛选 LibreOffice;查询失败必须使验收失败。
|
||||
|
||||
tests/scripts/lo-unregistered.sh:10 — PlistBuddy 的读取错误被当作声明键缺失。复现:在隔离副本中创建预期的 `LibreOffice.app/Contents/Info.plist`,内容写入 `this is invalid plist data`,运行检查仍退出 0 并打印成功。损坏的构建包配置因此通过结构验收。修复方向:先要求 plist 存在、可读且解析成功,再检查各声明键是否缺失;为损坏或缺失 plist 添加失败用例。
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:294 — 官方内核负向测试接受任意启动错误,无法证明提取出的内核实际经过版本与图块检查。复现:官方内核目录存在时,设置 `ELY_LOK_WORKER=/usr/bin/false`,单独运行 `client::tests::an_unpatched_build_is_rejected`,测试仍在 0.00 秒内通过,LibreOffice 完全未加载。独立实测提取出的官方内核能返回固定 BuildId,图块自检改写字节数为 0。修复方向:要求负向用例确实抵达该未打补丁的图块拒绝,并核对对应错误类型与原因;worker 启动失败、依赖缺失和版本错误应使该用例失败。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
scripts/inert-lo.sh:9 — plist 解析错误被吞掉,新增的 -10814 容错使损坏的构建包也报告成功。复现:在隔离副本的 LibreOffice.app/Contents/Info.plist 写入 `this is invalid plist data`,执行该脚本退出 0,打印 `claims no files and is unregistered`;同一副本运行 `tests/scripts/lo-unregistered.sh` 退出 1,报告 plist 解析失败。四个 PlistBuddy 查询的错误均被当成键缺失,lsregister 对损坏包返回的 -10814 又被第 16 行接受;调用此脚本的构建流程因此把无效包配置当成成功。修复方向:删除声明键和注销之前,先验证 Info.plist 存在、可读且解析成功,并覆盖损坏 plist 的失败用例。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/view/sheet.rs:26 — 打印坐标按当前 ppt 转成图块坐标,滚动锚点却继续沿用旧比例的图块 twips;每行截断产生的累计偏移随缩放改变,导致视口跳到另一批单元格,违反 LOK-04。实测使用 large.rs 的 SHORT_ROWS 样本、2 倍 DPI、1100×720 窗口,在 100% 下通过名称框跳到 B4998,可见行是 4988–5017;点击缩小到 80% 后,可见行变为 4923–4958,名称框仍为 B4998,选中格已移出视口。方向:缩放时先按旧比例把锚点还原为打印坐标,再按新比例换回图块坐标并重算滚动位置;增加真实工作表的大行号缩放锚点断言。证据:/tmp/r216-review-evidence.log。
|
||||
|
||||
crates/ely-lok/src/view/tests/large.rs:103 — R2.16 的拖选故障缺少回归保护;这里和 client/tests/rules.rs:507 的新增用例都只发送按下、抬起,并只断言单格地址。实测用临时 worker 代理把手势的移动及抬起坐标固定在按下处,两项 a_click_far_down 测试仍全部通过;同一代理下,真实窗口从 B4999 拖到 D5002,名称框仅得到 B4999,原版得到 B4999:D5002。方向:增加经过实际鼠标拖动路径的大行号区域选择用例,断言完整起止范围,并确认这种拖选失效变异会使测试失败。独立构建通过,原版 ely-lok 的 221 项测试和 ely-table 的 43 项测试通过;变异证据:/tmp/r216-drag-mutation.log、/tmp/r216-drag-native-mutation.log。
|
||||
|
||||
crates/ely-lok/src/client/tests/rules.rs:501 — 新增点击用例使文件从 487 行增长到 513 行,违反 agents.md 的每文件最多 500 行规则;wc -l 可直接复现。方向:按测试主题拆分用例,使各文件保持在 500 行以内。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
site/src/text.ts:94 — 截图翻译循环及第 20、57 行的 `shot` 文案已成为死代码,违反 agents.md 第 6 条。具体场景:打开首屏,依次进入六个应用锚点并切换中英文;浏览器探针在全部位置均得到 `[data-shot]` 数量 0、`[data-t=shot]` 数量 0,同时存在六个视频元素。搜索 `site/src`,`data-shot` 与 `dataset.shot` 仅在此循环出现,现有页面生成逻辑没有对应元素生成点。修复方向:删除过期的截图翻译循环及其两份文案。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
site/src/tour.ts:341:ResizeObserver 只观察 pin 与 hero,模型自身尺寸变化无法触发 moved,违背 R2.2 的模型尺寸变化后重新定位要求。复现:1280×1000、减少动效,聚焦 ElyPDF,将 .model .stage 的内联 width 与 height 同比缩至 80%,等待 300ms;建筑中心已变为 x=736.979,标签仍在 x=877.313,偏移约 140px。手动向 .model 派发 moved 后标签回到 x=736.969。修复方向:观察实际模型的尺寸变化,并用变化后的几何更新可见标签;补充独立模型缩放的回归断言。
|
||||
|
||||
site/src/touch.ts:37:添加 lifted 后立即测量,此时读取的是 translate 过渡开始时的几何;过渡结束没有再次定位,标签停留在旧位置。复现:1280×1000、普通动效,等待初始生长完成后聚焦 ElyDoc,再等待 300ms;标签底边 y=368.281,与抬升后的建筑顶边 y=368.282 几乎重合,按 place 的当前几何计算应为 y=328.566,误差约 39.7px。连续三次复现,手动派发 moved 后底边变为 y=328.563。修复方向:在建筑交互过渡期间及结束后同步标签位置,并覆盖普通动效下的聚焦与按压路径。
|
||||
|
||||
site/e2e/touch.spec.ts:160:新增定位断言只检查横向范围,缺少纵向位置与当前建筑顶边的关系;整组 reducedMotion 设置也跳过了上条缺陷涉及的过渡。复现:在临时测试副本的 beforeEach 中注入 .tour .caption { top: 0px !important; },标签固定在模型顶部,7 项 touch 测试仍全部通过,其中包括窗口变窄的新增测试。修复方向:断言标签的横向中心与纵向底边均对应当前几何,覆盖模型尺寸单独变化及普通动效结束,并要求该定位破坏探针使测试失败。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
site/src/touch.ts:94:用 SVG 元素宽度除以 viewBox 宽度计算一格的屏幕高度,忽略 preserveAspectRatio 按高度限制缩放的情况,模型尺寸变化后标签位置仍会偏离当前建筑几何。复现:1280×1000、减少动效,聚焦 ElyPDF,仅把 .model .stage 的内联 height 缩至原值的 80%,保持 width;等待 300ms,建筑顶边为 y=580.363,标签底边为 y=532.703,间距为 47.660px;getScreenCTM() 给出的当前一格高度为 31.772px,标签向上偏离 15.888px,连续三次结果相同。上一轮宽高同比缩放与普通动效结束后的场景已通过。修复方向:从 SVG 实际坐标变换计算纵向一格的屏幕尺寸,覆盖宽度、独立高度及同比尺寸变化。
|
||||
|
||||
site/e2e/touch.spec.ts:152:drift 重复实现中的宽度换算和静止命中层公式,断言与实际建筑顶边的关系缺少独立验证,会接受上条定位错误。复现:隔离副本中仅将第 172 行的尺寸操作改为 svg.style.height = `${svg.getBoundingClientRect().height * 0.8}px`,9 项 touch 测试全部通过,此时独立读取建筑轮廓与标签边界可见 15.888px 偏差。原版构建、13 项单测、38 项端到端测试均通过;上一轮 top: 0px !important 破坏探针现已使两项尺寸定位测试失败。修复方向:以实际渲染的建筑边界及独立坐标变换核对双轴位置,加入独立高度变化的回归,并要求上述偏差使断言失败。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/view/tests/input.rs:308:切页回归断言将 `entry.stale` 当作成功;旧图块标为过期后仍会被 `prepare` 绘制,因此该测试允许画布持续显示上一页。测试只塞入人工图块、调用 `show_part` 与 `flush`,全程没有驱动重绘或核对第二页内容。具体复现:在隔离副本的 `lane.rs:105` 后加入 `self.fetching.extend(self.tiles.keys().copied());`,使切页后的新图块请求被视为正在读取,`switching_slides_repaints_the_shown_tiles` 仍通过;实际驱动 `prepare` 并比对第二页像素的探针报 `new page tiles must finish repainting`。同一像素探针在原实现通过,且覆盖连续切页;原实现的 worker、ElyShow 构建及 43 项视图测试通过,真窗口点击也显示第二页。修复方向:用实际第一张幻灯片图块建立基线,切页后驱动绘制并等待新图块完成,核对其像素属于目标页,并让上述刷新受阻变异触发失败。复现日志:`/tmp/ely-r2.4-mutant-tests.log`;原实现像素验证:`/tmp/ely-r2.4-pixels.log`。探针源码副本与独立构建目录已删除。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
Reference in new issue
Block a user