Isaiah Odhner
d6705912b3
Fix DirectoryTree retaining real pathlib access
...
This fixes the following error:
----------------------------------------------- Captured stderr call -----------------------------------------------
╭───────────────────── Traceback (most recent call last) ──────────────────────╮
│ /home/io/Projects/textual-paint/src/textual_paint/file_dialogs.py:86 in │
│ on_mount │
│ │
│ 83 │ def on_mount(self) -> None: │
│ 84 │ │ """Called when the window is mounted.""" │
│ 85 │ │ self.content.mount( │
│ ❱ 86 │ │ │ EnhancedDirectoryTree(path="/"), │
│ 87 │ │ │ Horizontal( │
│ 88 │ │ │ │ Label(_("File name:")), │
│ 89 │ │ │ │ Input(classes="filename_input autofocus", value=self._ │
│ │
│ ╭──────────────────── locals ────────────────────╮ │
│ │ self = OpenDialogWindow(id='window_auto_id_0') │ │
│ ╰────────────────────────────────────────────────╯ │
│ │
│ in __init__:119 │
│ │
│ 116 │ │ self._load_queue: Queue[TreeNode[DirEntry]] = Queue() │
│ 117 │ │ super().__init__( │
│ 118 │ │ │ str(path), │
│ ❱ 119 │ │ │ data=DirEntry(self.PATH(path)), │
│ 120 │ │ │ name=name, │
│ 121 │ │ │ id=id, │
│ 122 │ │ │ classes=classes, │
│ │
│ ╭───────────────── locals ──────────────────╮ │
│ │ classes = None │ │
│ │ disabled = False │ │
│ │ id = None │ │
│ │ name = None │ │
│ │ path = '/' │ │
│ │ self = EnhancedDirectoryTree( │ │
│ │ │ id='some fake shit', │ │
│ │ │ name='some more fake shit' │ │
│ │ ) │ │
│ ╰───────────────────────────────────────────╯ │
│ │
│ in __new__:960 │
│ │
│ 957 │ def __new__(cls, *args, **kwargs): │
│ 958 │ │ if cls is Path: │
│ 959 │ │ │ cls = WindowsPath if os.name == 'nt' else PosixPath │
│ ❱ 960 │ │ self = cls._from_parts(args) │
│ 961 │ │ if not self._flavour.is_supported: │
│ 962 │ │ │ raise NotImplementedError("cannot instantiate %r on your │
│ 963 │ │ │ │ │ │ │ │ │ % (cls.__name__,)) │
│ │
│ ╭──────────── locals ─────────────╮ │
│ │ args = ('/',) │ │
│ │ cls = <class 'pathlib.Path'> │ │
│ │ kwargs = {} │ │
│ ╰─────────────────────────────────╯ │
│ │
│ in _from_parts:594 │
│ │
│ 591 │ │ # We need to call _parse_args on the instance, so as to get t │
│ 592 │ │ # right flavour. │
│ 593 │ │ self = object.__new__(cls) │
│ ❱ 594 │ │ drv, root, parts = self._parse_args(args) │
│ 595 │ │ self._drv = drv │
│ 596 │ │ self._root = root │
│ 597 │ │ self._parts = parts │
│ │
│ ╭──────────────────────────── locals ─────────────────────────────╮ │
│ │ args = ('/',) │ │
│ │ cls = <class 'pathlib.Path'> │ │
│ │ self = <repr-error "'Path' object has no attribute '_flavour'"> │ │
│ ╰─────────────────────────────────────────────────────────────────╯ │
│ │
│ in _parse_args:587 │
│ │
│ 584 │ │ │ │ │ │ "argument should be a str object or an os.Pat │
│ 585 │ │ │ │ │ │ "object returning str, not %r" │
│ 586 │ │ │ │ │ │ % type(a)) │
│ ❱ 587 │ │ return cls._flavour.parse_parts(parts) │
│ 588 │ │
│ 589 │ @classmethod │
│ 590 │ def _from_parts(cls, args): │
│ │
│ ╭──────────── locals ────────────╮ │
│ │ a = '/' │ │
│ │ args = ('/',) │ │
│ │ cls = <class 'pathlib.Path'> │ │
│ │ parts = ['/'] │ │
│ ╰────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────╯
AttributeError: type object 'Path' has no attribute '_flavour'
...which was itself masked by an error:
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/_doc.py:136: in take_svg_screenshot
app.run(
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/app.py:1207: in run
asyncio.run(run_app())
/usr/lib/python3.10/asyncio/runners.py:44: in run
return loop.run_until_complete(main)
/usr/lib/python3.10/asyncio/base_events.py:649: in run_until_complete
return future.result()
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/app.py:1196: in run_app
await self.run_async(
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/app.py:1168: in run_async
await app._shutdown()
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/app.py:2186: in _shutdown
await self._close_all()
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/app.py:2166: in _close_all
await self._prune_node(stack_screen)
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/app.py:2656: in _prune_node
await asyncio.gather(*close_messages)
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/message_pump.py:453: in _close_messages
await self._task
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/message_pump.py:469: in _process_messages
await self._pre_process()
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/message_pump.py:490: in _pre_process
self.app._handle_exception(error)
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/app.py:1822: in _handle_exception
self._fatal_error()
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/app.py:1827: in _fatal_error
traceback = Traceback(
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/rich/traceback.py:264: in __init__
trace = self.extract(
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/rich/traceback.py:449: in extract
locals={
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/rich/traceback.py:450: in <dictcomp>
key: pretty.traverse(
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/rich/pretty.py:852: in traverse
node = _traverse(_object, root=True)
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/rich/pretty.py:647: in _traverse
args = list(iter_rich_args(rich_repr_result))
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/rich/pretty.py:614: in iter_rich_args
for arg in rich_args:
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/widget.py:2516: in __rich_repr__
yield "id", self.id, None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <[AttributeError("'EnhancedDirectoryTree' object has no attribute '_id'") raised in repr()] EnhancedDirectoryTree object at 0x7f06b38c89d0>
@property
def id(self) -> str | None:
"""The ID of this node, or None if the node has no ID."""
> return self._id
E AttributeError: 'EnhancedDirectoryTree' object has no attribute '_id'. Did you mean: 'id'?
/home/io/Projects/textual-paint/.venv/lib/python3.10/site-packages/textual/dom.py:483: AttributeError
...which I worked around by adding class vars to EnhancedDirectoryTree:
_id = "some fake shit"
_name = "some more fake shit"
It was also a heisenbug. I was able to run the tests, even update snapshots, running in debug mode in VS Code.
2023-09-12 19:45:41 -04:00
Isaiah Odhner
dce55cd833
Fix further errors with pyfakefs
2023-09-12 19:45:41 -04:00
Isaiah Odhner
7f91138a63
Use pyfakefs for file dialog tests
...
- The app's directory structure is not a constant, and shouldn't play into this test. Aside from codebase restructuring, directories like `__pycache__` can come and go.
- Even if a temporary directory were created with files enough to fill the view, the scrollbar would still change based on the folder structure outside of the temporary folder.
- pyfakefs is one way to ensure a consistent view of a folder structure for testing. It allows adding real folders in a readonly way. It's more complicated than I thought it would be going in, since I had to add workarounds for pyfiglet and pytest-textual-snapshot, and handle an edge case in my EnhancedDirectoryTree (which got an error which seems to be swallowed?), not to mention pyfakefs raises an error saying "No such file or directory in the fake filesystem" when actually it's the real directory not existing when trying to add it to the fake filesystem, and VS Code was hiding stack frames and refusing to step into library code, and it turned out that I was resolving the absolute path wrong, but it looked right to me because the only part that was missing was "textual-paint", when, at a glance it seemed present, since the "textual_paint" part was present. Ay-ay-ay!
- I don't know if this will fix the problem I saw where these tests' snapshots all changed with no visual or even structural changes, just the IDs of elements changing. I don't know what caused that.
Oh yeah and this is still actually a problem:
============================================= short test summary info ==============================================
FAILED tests/test_snapshots.py::test_paint_open_dialog[light_unicode] - AttributeError: 'EnhancedDirectoryTree' object has no attribute '_id'. Did you mean: 'id'?
FAILED tests/test_snapshots.py::test_paint_open_dialog[dark_unicode] - AttributeError: 'EnhancedDirectoryTree' object has no attribute '_id'. Did you mean: 'id'?
FAILED tests/test_snapshots.py::test_paint_open_dialog[light_ascii] - AttributeError: 'EnhancedDirectoryTree' object has no attribute '_id'. Did you mean: 'id'?
FAILED tests/test_snapshots.py::test_paint_open_dialog[dark_ascii] - AttributeError: 'EnhancedDirectoryTree' object has no attribute '_id'. Did you mean: 'id'?
FAILED tests/test_snapshots.py::test_paint_save_dialog[light_unicode] - AttributeError: 'EnhancedDirectoryTree' object has no attribute '_id'. Did you mean: 'id'?
FAILED tests/test_snapshots.py::test_paint_save_dialog[dark_unicode] - AttributeError: 'EnhancedDirectoryTree' object has no attribute '_id'. Did you mean: 'id'?
FAILED tests/test_snapshots.py::test_paint_save_dialog[light_ascii] - AttributeError: 'EnhancedDirectoryTree' object has no attribute '_id'. Did you mean: 'id'?
FAILED tests/test_snapshots.py::test_paint_save_dialog[dark_ascii] - AttributeError: 'EnhancedDirectoryTree' object has no attribute '_id'. Did you mean: 'id'?
========================== 8 failed, 56 passed, 1 xfailed, 1 warning in 152.86s (0:02:32) ==========================
It worked when running in debug, but not when running normally.
2023-09-12 19:45:41 -04:00
Isaiah Odhner
370b497cc6
VS Code: Add launch configuration for debugging tests
...
I just want to disable the external code skipping feature ("justMyCode")
but I seem to have to abandon the pytest integration to do so?
2023-09-12 19:45:41 -04:00
Isaiah Odhner
bb8e968f6c
Test Free-Form Select off-screen melding bug
2023-09-12 19:45:41 -04:00
Isaiah Odhner
48b15319da
Test Select tool
2023-09-11 22:42:10 -04:00
Isaiah Odhner
2206df20bd
Test Free-Form Select tool
2023-09-11 22:37:30 -04:00
Isaiah Odhner
ad86491ada
Shorten Offset arguments
2023-09-11 22:36:31 -04:00
Isaiah Odhner
42e3104c7e
Tighten Polygon test timing
...
and find the tool button by tooltip text
2023-09-11 22:25:38 -04:00
Isaiah Odhner
ec774c264a
Test Polygon tool!
2023-09-11 22:11:02 -04:00
Isaiah Odhner
e849af1dcd
Add to todos
2023-09-11 21:42:09 -04:00
Isaiah Odhner
304f18c3f4
Import helpers instead of including them in generated code
2023-09-11 21:42:09 -04:00
Isaiah Odhner
85d601b262
Refactor tests that click a tool button
2023-09-11 21:16:13 -04:00
Isaiah Odhner
6e7a3445e0
DRY widget clicking helpers (use one in the other)
2023-09-11 20:23:15 -04:00
Isaiah Odhner
95d89a6e7f
Move Pilot test helpers to a file
2023-09-11 19:40:20 -04:00
Isaiah Odhner
7f379103a6
Accept new Text tool test snapshots
2023-09-11 19:35:04 -04:00
Isaiah Odhner
0d9f62a4c3
Skip polygon test for now
2023-09-11 19:24:41 -04:00
Isaiah Odhner
6c108294b8
Add modifier options to click_by_index
(not recorded yet)
2023-09-11 19:12:26 -04:00
Isaiah Odhner
d6399022ee
Fix/cleanup test_text_tool_cursor_keys_and_color
...
- Fix `APP_PATH` reference error
- Hold Ctrl to set the foreground color (modifiers not recorded yet)
- DRY `pilot.press`
- Use same terminal size as many other tests
- Seven isn't spelt with a 2, but zero is! ;)
2023-09-11 19:10:18 -04:00
Isaiah Odhner
3b76ba0997
Accept mysterious ID changes to file dialog snapshots
...
WITHOUT accepting new WIP Polygon and Text tool tests
2023-09-11 18:45:21 -04:00
Isaiah Odhner
5426ed14a3
Use semantic key synonym
2023-09-11 18:02:20 -04:00
Isaiah Odhner
155b0e4c99
Wait only at start of playback, not before each drag/click in test
2023-09-11 18:02:20 -04:00
Isaiah Odhner
2b447a06d6
WIP: Polygon and Text tool tests
2023-09-11 18:02:20 -04:00
Isaiah Odhner
58ddc7e856
Add a word in docstring
...
I would've named the class `TestRecorder` if the file name `test_recorder.py` wouldn't be picked up by pytest as a test file...
2023-09-11 18:02:20 -04:00
Isaiah Odhner
2d03709565
Rename placeholders to sound less hand-wavy
2023-09-11 18:02:20 -04:00
Isaiah Odhner
a844cd7967
Make output_file
a parameter; add __init__
docstring
2023-09-11 18:02:20 -04:00
Isaiah Odhner
405cc1bf4e
Make test recorder generic to apps
...
The only mention of Paint is at construction time now.
2023-09-11 18:02:20 -04:00
Isaiah Odhner
c5e96ef580
Add todos to module-level docstring
2023-09-11 18:02:20 -04:00
Isaiah Odhner
021bc8a93c
Simplify escaping of triple quotes
2023-09-11 18:02:20 -04:00
Isaiah Odhner
8614787e3d
Add newline to separate related code
2023-09-11 18:02:20 -04:00
Isaiah Odhner
340c7ce74e
Add Ctrl+R to restart and replay the recording
2023-09-11 18:02:20 -04:00
Isaiah Odhner
84ff381f04
This might handle multi-screen apps (untested)
2023-09-11 18:02:20 -04:00
Isaiah Odhner
f62c8ff6c9
Highlight active step during playback
...
I love when GitHub Copilot suggests code using an API (or language feature / idiom etc.) that I didn't even know about.
More often it'll just make something up, but `highlight_lines` actually exists!
Anyways, I'm doing a hacky thing to get it to highlight lines of code during execution,
prepending code (invisibly) to each line, except lines that would cause a syntax error.
Also, the steps view isn't updating reliably (even when adding steps).
2023-09-11 18:02:20 -04:00
Isaiah Odhner
5e55846fb2
Add steps view
...
This isn't very useful yet without highlighting of the current step in playback.
2023-09-11 18:02:20 -04:00
Isaiah Odhner
b590d807a7
Add todo/thoughts about drag vs click ambiguity
2023-09-11 18:02:20 -04:00
Isaiah Odhner
5b0d8c703a
Refactor: use set instead of dict for helper codes
...
Don't need arbitrary IDs for them.
2023-09-11 18:02:20 -04:00
Isaiah Odhner
87c9c192e0
Disable event comments in generated code
2023-09-11 18:02:20 -04:00
Isaiah Odhner
196ae0e810
Speed up drags during playback
2023-09-11 18:02:20 -04:00
Isaiah Odhner
1a53a18912
Fix IndexError when undoing all the way
2023-09-11 18:02:20 -04:00
Isaiah Odhner
402b46c7df
Make failing to create a selector non-fatal
2023-09-11 18:02:20 -04:00
Isaiah Odhner
d6d9ddba67
Add a todo
2023-09-11 18:02:20 -04:00
Isaiah Odhner
9739422936
Show every event with comments in generated code
2023-09-11 18:02:20 -04:00
Isaiah Odhner
baa2b8cbc6
Fix batch undoing for drags
2023-09-11 18:02:20 -04:00
Isaiah Odhner
0e9f6d28a7
Implement recording/replaying drags
2023-09-11 18:02:20 -04:00
Isaiah Odhner
52f319da24
Refactor: split method
...
(This is probably pointless.)
2023-09-11 17:59:56 -04:00
Isaiah Odhner
82a5b201d4
WIP: make test recorder more generic to apps
2023-09-11 17:59:56 -04:00
Isaiah Odhner
2fb9e64c4f
DRY generated code with a helper function
2023-09-11 17:59:56 -04:00
Isaiah Odhner
b9503e44f4
This is somewhat more reliable (maybe just due to the delay....)
2023-09-11 17:59:56 -04:00
Isaiah Odhner
2a7b4412e1
Add docstrings, rename method
...
The new method name reflects the fact that it handles some input as commands, not just recording the events.
2023-09-11 17:59:56 -04:00
Isaiah Odhner
96ef2dfdff
Whitespace: organize imports
2023-09-11 17:59:56 -04:00