|
1 | | -"""Physical-mouse coverage for the panel and for the screen-to-world contract. |
| 1 | +"""Physical-mouse coverage for the screen-to-world contract. |
2 | 2 |
|
3 | 3 | Most scenarios reach the editors through the typed control channel, which never |
4 | | -touches the engine's mouse callins. These two do the opposite: every input here |
5 | | -is a real X11 event, and the assertions read the resulting state back through |
6 | | -the control channel. They carry no goldens, so they stay cheap enough to run on |
7 | | -every suite. |
| 4 | +touches the engine's mouse callins at all. This one does the opposite: the input |
| 5 | +is real X11, and the result is read back through the control channel. It carries |
| 6 | +no golden, so a vertical flip is reported as two coordinates rather than as a |
| 7 | +picture full of moved trees. |
8 | 8 | """ |
9 | 9 |
|
10 | 10 | from typing import TYPE_CHECKING |
11 | 11 |
|
12 | 12 | from e2e.driver.timing import Delay |
13 | 13 | from e2e.scenarios.helpers.camera import zoom_map |
14 | | -from e2e.scenarios.helpers.geometry import ( |
15 | | - MAP, |
16 | | - OBJECTS, |
17 | | - TAB_X, |
18 | | - TAB_Y, |
19 | | - editor_point, |
20 | | - panel_left, |
21 | | - panel_point, |
22 | | - window_size, |
23 | | -) |
| 14 | +from e2e.scenarios.helpers.geometry import OBJECTS, panel_point, window_size |
24 | 15 | from e2e.scenarios.helpers.objects import arm_tree as _arm_tree |
25 | 16 | from e2e.scenarios.helpers.objects import open_object_editor as _open |
26 | 17 | from e2e.scenarios.helpers.registry import scenario |
|
30 | 21 | from e2e.driver.state import RunState |
31 | 22 |
|
32 | 23 | # A click and the ray query for the same pixel describe the same spot. The |
33 | | -# budget covers the brush's own rounding, not a different point on the map: a |
34 | | -# mirrored y lands hundreds of units away even near the middle of the view. |
| 24 | +# budget covers the placement's own rounding, not a different point on the map: |
| 25 | +# a mirrored y lands hundreds of units away even near the middle of the view. |
35 | 26 | TRACE_TOLERANCE = 40.0 |
36 | 27 |
|
37 | 28 |
|
38 | | -@scenario() |
39 | | -def mouse_ui(run_state: "RunState") -> None: |
40 | | - """The panel, driven only by the pointer. |
41 | | -
|
42 | | - Tab, editor button, field click, typed commit and a numeric drag are all |
43 | | - real X11 input, and the editor model is read back to prove each one landed |
44 | | - on the control it aimed at. Without this, every editor is reachable in tests |
45 | | - only through the control channel, which no user has. |
46 | | -
|
47 | | - Note what this cannot cover: RmlUi consumes a press over the panel document |
48 | | - inside the engine, so those clicks never reach the native mouse callin at |
49 | | - all. The panel's own hit test therefore sees map presses only, and this |
50 | | - scenario proves the user-facing path rather than that hit test. |
51 | | - """ |
52 | | - run_state.focus() |
53 | | - left = panel_left(run_state) |
54 | | - run_state.click(left + TAB_X["map"], TAB_Y, delay=Delay.FRAME) |
55 | | - run_state.click(*editor_point(left, "map", "terrain"), delay=Delay.READY) |
56 | | - |
57 | | - terrain = run_state.control.editor("heightmapEditor") |
58 | | - run_state.fill_text( |
59 | | - *panel_point(left, MAP["terrain_size"]), |
60 | | - "140", |
61 | | - click_delay=Delay.FRAME, |
62 | | - commit_delay=Delay.SETTLE, |
63 | | - ) |
64 | | - typed = terrain.get("size") |
65 | | - assert typed == 140.0, f"typing into the Size field gave {typed!r}, want 140.0" |
66 | | - |
67 | | - # A numeric drag pins the pointer and warps it back, so the motion has to be |
68 | | - # relative. The direction is what matters: the exact value depends on which |
69 | | - # tick the release meets. |
70 | | - run_state.press(*panel_point(left, MAP["terrain_size"])) |
71 | | - run_state.move_relative(60) |
72 | | - run_state.release(*panel_point(left, MAP["terrain_size"]), delay=Delay.SETTLE) |
73 | | - dragged = terrain.get("size") |
74 | | - assert isinstance(dragged, float), f"Size is {dragged!r}, want a number" |
75 | | - assert dragged > typed, f"dragging right did not raise Size: {typed} -> {dragged}" |
76 | | - |
77 | | - # A press beside the panel belongs to the map, not to the control that was |
78 | | - # last touched. This is the ownership half of the hit test. |
79 | | - run_state.fill_text( |
80 | | - *panel_point(left, MAP["terrain_rotation"]), |
81 | | - "15", |
82 | | - click_delay=Delay.FRAME, |
83 | | - commit_delay=Delay.SETTLE, |
84 | | - ) |
85 | | - rotation = terrain.get("rotation") |
86 | | - assert rotation == 15.0, f"the second field took {rotation!r}, want 15.0" |
87 | | - assert terrain.get("size") == dragged, "editing Rotation moved Size" |
88 | | - |
89 | | - |
90 | 29 | @scenario() |
91 | 30 | def mouse_coordinates(run_state: "RunState") -> None: |
92 | 31 | """A click places an object where that pixel points. |
93 | 32 |
|
94 | 33 | The engine reports mouse callbacks in its own bottom-origin screen space, |
95 | 34 | while control clients address the window top-origin like a screenshot. Both |
96 | | - paths are asked about the same two pixels here, so a future flip on either |
97 | | - side fails immediately -- and says so, rather than moving a golden's trees. |
| 35 | + paths are asked about the same two pixels here, so a flip on either side |
| 36 | + fails immediately and names the two positions it compared. |
| 37 | +
|
| 38 | + This is deliberately the only mouse scenario. The panel and the consoles do |
| 39 | + their hit testing inside RmlUi, which the engine feeds directly: a press |
| 40 | + over either never reaches the native callin, and their ownership checks are |
| 41 | + reached through a pointer-capture state that a scripted click cannot steer. |
| 42 | + A UI-side mouse test therefore passes whatever the coordinate space is, |
| 43 | + while this one cannot. |
98 | 44 | """ |
99 | 45 | run_state.focus() |
100 | 46 | left = _open(run_state, "features") |
|
0 commit comments