nvda-addon-testkit 1.0.0__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/PKG-INFO +18 -2
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/README.md +17 -1
- nvda_addon_testkit-1.1.0/docs/modules/ROOT/examples/tests_e2e/test_demo_dsl.py +63 -0
- nvda_addon_testkit-1.1.0/docs/modules/ROOT/examples/tests_e2e/test_dsl_probes.py +29 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/examples/tests_e2e/test_smoke.py +13 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/nav.adoc +2 -0
- nvda_addon_testkit-1.1.0/docs/modules/ROOT/pages/guide/dsl.adoc +154 -0
- nvda_addon_testkit-1.1.0/docs/modules/ROOT/pages/guide/modal-dialogs.adoc +48 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/__init__.py +1 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/eval_api.py +29 -0
- nvda_addon_testkit-1.1.0/spy/globalPlugins/nvda_testkit_spy/modal_api.py +229 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/_spy/nvda-testkit-spy.nvda-addon +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/_version.py +2 -2
- nvda_addon_testkit-1.1.0/src/nvda_testkit/actionmark.py +20 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/client.py +76 -3
- nvda_addon_testkit-1.1.0/src/nvda_testkit/dsl/__init__.py +5 -0
- nvda_addon_testkit-1.1.0/src/nvda_testkit/dsl/dialogs.py +60 -0
- nvda_addon_testkit-1.1.0/src/nvda_testkit/dsl/hearing.py +56 -0
- nvda_addon_testkit-1.1.0/src/nvda_testkit/dsl/lifecycle.py +65 -0
- nvda_addon_testkit-1.1.0/src/nvda_testkit/dsl/logsteps.py +67 -0
- nvda_addon_testkit-1.1.0/src/nvda_testkit/dsl/matching.py +44 -0
- nvda_addon_testkit-1.1.0/src/nvda_testkit/dsl/messages.py +105 -0
- nvda_addon_testkit-1.1.0/src/nvda_testkit/dsl/nvda.py +271 -0
- nvda_addon_testkit-1.1.0/src/nvda_testkit/dsl/waiting.py +26 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/namespaces/keys.py +15 -3
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/plugin.py +13 -2
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/rpcclient.py +1 -1
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/settings.py +21 -5
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/conftest.py +40 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/fake_nvda.py +41 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_client.py +80 -0
- nvda_addon_testkit-1.1.0/tests/test_client_action_mark.py +143 -0
- nvda_addon_testkit-1.1.0/tests/test_dsl_dialogs.py +181 -0
- nvda_addon_testkit-1.1.0/tests/test_dsl_expecting.py +70 -0
- nvda_addon_testkit-1.1.0/tests/test_dsl_hearing.py +111 -0
- nvda_addon_testkit-1.1.0/tests/test_dsl_lifecycle.py +126 -0
- nvda_addon_testkit-1.1.0/tests/test_dsl_logsteps.py +97 -0
- nvda_addon_testkit-1.1.0/tests/test_dsl_messages.py +159 -0
- nvda_addon_testkit-1.1.0/tests/test_dsl_nvda.py +88 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_plugin.py +53 -1
- nvda_addon_testkit-1.1.0/tests/test_settings.py +126 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_eval_api.py +46 -2
- nvda_addon_testkit-1.1.0/tests_spy/test_modal_api.py +203 -0
- nvda_addon_testkit-1.0.0/tests/test_settings.py +0 -58
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/CODEOWNERS +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/CONTRIBUTING.md +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/pull_request_template.md +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/renovate.json +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/CI.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/notify-docs.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/prepare-release.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/project-sync.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/publish-python.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/sonar-fork-coverage.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/sonar-fork-scan.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/sonar.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/stale.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.github/workflows/zizmor.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.gitignore +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/.sonarlint/connectedMode.json +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/LICENSE +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/action.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/antora.yml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/examples/demo-addon/build.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/examples/demo-addon/globalPlugins/testkit_demo.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/examples/demo-addon/manifest.ini +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/examples/tests_e2e/conftest.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/examples/tests_e2e/test_demo_addon.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/examples/tests_e2e/test_modal_dialog_investigation.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/ci-guide.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/contributing.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/example-project.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/guide/addons.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/guide/braille.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/guide/config.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/guide/fixtures.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/guide/keys.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/guide/log.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/guide/speech.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/index.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/installation.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/troubleshooting.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/docs/modules/ROOT/pages/tutorial.adoc +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/pyproject.toml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/ruff.toml +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/scripts/next-version.sh +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/sonar-project.properties +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/addons_api.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/braille_tap.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/config_api.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/input_api.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/log_tap.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/mainthread.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/registry.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/serialise.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/server.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/speech_tap.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/manifest.ini +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/__init__.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/cli.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/download.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/errors.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/namespaces/__init__.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/namespaces/addons.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/namespaces/braille.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/namespaces/config.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/namespaces/log.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/namespaces/speech.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/portable.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/process.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/provisioning.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/resolve.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/speechtypes.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/src/nvda_testkit/spybundle.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/fixtures/snapshot_index_alpha.html +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/fixtures/update_check_stable.txt +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_addons_namespace.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_braille_namespace.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_build_spy.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_cli.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_config_namespace.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_download.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_e2e_conftest.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_errors.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_fake_nvda.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_keys_namespace.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_log_namespace.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_portable.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_process.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_provisioning.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_resolve.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_rpcclient.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_speech_namespace.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests/test_speechtypes.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/__init__.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/conftest.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/nvda_stubs.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_addons_api.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_braille_tap.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_config_api.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_input_api.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_log_tap.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_mainthread.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_plugin.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_registry.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_serialise.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_server.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tests_spy/test_speech_tap.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/tools/build_spy.py +0 -0
- {nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: nvda-addon-testkit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: End-to-end testing for NVDA add-ons against a real NVDA
|
|
5
5
|
Project-URL: Homepage, https://github.com/ZirekHQ/nvda-addon-testkit
|
|
6
6
|
Project-URL: Issues, https://github.com/ZirekHQ/nvda-addon-testkit/issues
|
|
@@ -44,6 +44,15 @@ def test_my_addon_announces_itself(nvda, addon_under_test):
|
|
|
44
44
|
nvda.log.assert_no_errors()
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
The same test reads as plain steps with the (experimental) DSL:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
def test_my_addon_announces_itself(nvda, addon_under_test):
|
|
51
|
+
nvda.press("NVDA+shift+m")
|
|
52
|
+
nvda.should_hear("my add-on is ready")
|
|
53
|
+
nvda.should_have_no_errors()
|
|
54
|
+
```
|
|
55
|
+
|
|
47
56
|
## Install
|
|
48
57
|
|
|
49
58
|
```bash
|
|
@@ -106,6 +115,13 @@ exercise NVDA's own restart logic. For that, use `nvda.restart_nvda()`,
|
|
|
106
115
|
which triggers NVDA's real `core.restart()` and waits for the replacement
|
|
107
116
|
process — needs `--nvda-allow-eval`, since it is built on `nvda.eval()`.
|
|
108
117
|
|
|
118
|
+
A real `wx.Dialog.ShowModal()` never returns control to any of the above —
|
|
119
|
+
NVDA's main-thread queue doesn't drain while one is up. Open it with
|
|
120
|
+
`nvda.exec_nowait()` instead of `exec()` (queues the scenario without
|
|
121
|
+
waiting for it to finish), then close it with `nvda.simulate_modal(gesture,
|
|
122
|
+
timeout=10.0)`, which sends real injected keyboard input once our process
|
|
123
|
+
takes the foreground.
|
|
124
|
+
|
|
109
125
|
## Requirements
|
|
110
126
|
|
|
111
127
|
Windows to run the tests. NVDA is downloaded automatically — you do not need
|
|
@@ -138,4 +154,4 @@ If this repository saves you time and effort, please consider supporting it!
|
|
|
138
154
|
|
|
139
155
|
- ⭐ [Star on GitHub](https://github.com/ZirekHQ/nvda-addon-testkit)
|
|
140
156
|
- 🐦 [Share on Twitter](https://twitter.com/intent/tweet?text=nvda-addon-testkit%20-%20real%20end-to-end%20testing%20for%20NVDA%20add-ons&url=https%3A%2F%2Fgithub.com%2FZirekHQ%2Fnvda-addon-testkit)
|
|
141
|
-
- 💖 [
|
|
157
|
+
- 💖 [Support on Open Collective](https://opencollective.com/zirek)
|
|
@@ -17,6 +17,15 @@ def test_my_addon_announces_itself(nvda, addon_under_test):
|
|
|
17
17
|
nvda.log.assert_no_errors()
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
The same test reads as plain steps with the (experimental) DSL:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
def test_my_addon_announces_itself(nvda, addon_under_test):
|
|
24
|
+
nvda.press("NVDA+shift+m")
|
|
25
|
+
nvda.should_hear("my add-on is ready")
|
|
26
|
+
nvda.should_have_no_errors()
|
|
27
|
+
```
|
|
28
|
+
|
|
20
29
|
## Install
|
|
21
30
|
|
|
22
31
|
```bash
|
|
@@ -79,6 +88,13 @@ exercise NVDA's own restart logic. For that, use `nvda.restart_nvda()`,
|
|
|
79
88
|
which triggers NVDA's real `core.restart()` and waits for the replacement
|
|
80
89
|
process — needs `--nvda-allow-eval`, since it is built on `nvda.eval()`.
|
|
81
90
|
|
|
91
|
+
A real `wx.Dialog.ShowModal()` never returns control to any of the above —
|
|
92
|
+
NVDA's main-thread queue doesn't drain while one is up. Open it with
|
|
93
|
+
`nvda.exec_nowait()` instead of `exec()` (queues the scenario without
|
|
94
|
+
waiting for it to finish), then close it with `nvda.simulate_modal(gesture,
|
|
95
|
+
timeout=10.0)`, which sends real injected keyboard input once our process
|
|
96
|
+
takes the foreground.
|
|
97
|
+
|
|
82
98
|
## Requirements
|
|
83
99
|
|
|
84
100
|
Windows to run the tests. NVDA is downloaded automatically — you do not need
|
|
@@ -111,4 +127,4 @@ If this repository saves you time and effort, please consider supporting it!
|
|
|
111
127
|
|
|
112
128
|
- ⭐ [Star on GitHub](https://github.com/ZirekHQ/nvda-addon-testkit)
|
|
113
129
|
- 🐦 [Share on Twitter](https://twitter.com/intent/tweet?text=nvda-addon-testkit%20-%20real%20end-to-end%20testing%20for%20NVDA%20add-ons&url=https%3A%2F%2Fgithub.com%2FZirekHQ%2Fnvda-addon-testkit)
|
|
114
|
-
- 💖 [
|
|
130
|
+
- 💖 [Support on Open Collective](https://opencollective.com/zirek)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""The demo add-on driven through the DSL. Each tagged block is a docs example.
|
|
2
|
+
|
|
3
|
+
This file assumes the demo add-on is not installed when it starts: test_demo_addon.py,
|
|
4
|
+
sorted earlier, uninstalls it last.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
# Mirrors RUNNER_ENVIRONMENT_ERRORS in tests_e2e/conftest.py.
|
|
10
|
+
RUNNER_NOISE = (
|
|
11
|
+
r"nvwave|WASAPI|audio (?:device|output|session|endpoint)",
|
|
12
|
+
r"synthDriver|synthesi[sz]|espeak|oneCore|SAPI",
|
|
13
|
+
r"braille ?display|brailleDisplayDriver|brailleInput",
|
|
14
|
+
r"UIAHandler|IAccessible|interactive desktop|desktop object",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.fresh_nvda
|
|
19
|
+
def test_install_and_remove_are_one_step_each(nvda):
|
|
20
|
+
# tag::dsl-lifecycle[]
|
|
21
|
+
nvda.should_have_addon("testkit-demo", "not installed")
|
|
22
|
+
nvda.install_addon()
|
|
23
|
+
nvda.should_have_addon("testkit-demo", "enabled")
|
|
24
|
+
nvda.remove_addon("testkit-demo")
|
|
25
|
+
nvda.should_have_addon("testkit-demo", "not installed")
|
|
26
|
+
# end::dsl-lifecycle[]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_the_gesture_announces_the_phrase(nvda):
|
|
30
|
+
nvda.install_addon()
|
|
31
|
+
# tag::dsl-basic[]
|
|
32
|
+
nvda.press("NVDA+shift+control+d")
|
|
33
|
+
nvda.should_hear("testkit demo says hello")
|
|
34
|
+
# end::dsl-basic[]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_startup_logs_the_loaded_message(nvda):
|
|
38
|
+
nvda.install_addon()
|
|
39
|
+
nvda.relaunch()
|
|
40
|
+
nvda.should_log("testkit demo add-on loaded", within=20)
|
|
41
|
+
nvda.should_have_no_errors(ignoring=list(RUNNER_NOISE))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_the_block_form_waits_for_the_speech_a_step_causes(nvda):
|
|
45
|
+
nvda.install_addon()
|
|
46
|
+
# tag::dsl-expecting[]
|
|
47
|
+
with nvda.expecting_speech("testkit demo says hello"):
|
|
48
|
+
nvda.press("NVDA+shift+control+d")
|
|
49
|
+
# end::dsl-expecting[]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_a_real_dialog_opens_and_closes_in_a_block(require_eval, nvda):
|
|
53
|
+
# tag::dsl-dialog[]
|
|
54
|
+
with nvda.dialog(
|
|
55
|
+
"import wx\n"
|
|
56
|
+
"dlg = wx.MessageDialog(None, 'confirm?', 'confirm?', wx.YES_NO)\n"
|
|
57
|
+
"dlg.ShowModal()\n"
|
|
58
|
+
"dlg.Destroy()\n",
|
|
59
|
+
close_with="enter",
|
|
60
|
+
):
|
|
61
|
+
pass
|
|
62
|
+
# end::dsl-dialog[]
|
|
63
|
+
nvda.wait_until_idle(timeout=15)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Probes for DSL behaviour not yet verified on real NVDA."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
DIALOG = (
|
|
6
|
+
"import wx\n"
|
|
7
|
+
"dlg = wx.MessageDialog(None, 'confirm?', 'confirm?', wx.YES_NO)\n"
|
|
8
|
+
"dlg.ShowModal()\n"
|
|
9
|
+
"dlg.Destroy()\n"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_speech_and_log_reads_work_while_a_modal_is_open(require_eval, nvda):
|
|
14
|
+
nvda.open_dialog(DIALOG)
|
|
15
|
+
try:
|
|
16
|
+
assert isinstance(nvda.speech.index(), int)
|
|
17
|
+
assert isinstance(nvda.log.all(), list)
|
|
18
|
+
finally:
|
|
19
|
+
nvda.close_dialog("enter")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.mark.parametrize("text", ["Hello", "a.b,c", "x!y"])
|
|
23
|
+
def test_typing_characters_beyond_lowercase_letters(nvda, text):
|
|
24
|
+
nvda.type(text)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.mark.xfail(strict=False, reason="non-ASCII gesture names are unverified")
|
|
28
|
+
def test_typing_non_ascii_characters(nvda):
|
|
29
|
+
nvda.type("ünï")
|
|
@@ -46,3 +46,16 @@ def test_restart_nvda_exercises_core_restart(require_eval, nvda, assert_no_unexp
|
|
|
46
46
|
nvda.restart_nvda(timeout=60)
|
|
47
47
|
assert nvda.process.handshake.pid != old_pid
|
|
48
48
|
assert_no_unexpected_errors(nvda)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_simulate_modal_closes_a_real_dialog(require_eval, nvda):
|
|
52
|
+
# tag::modal[]
|
|
53
|
+
nvda.exec_nowait(
|
|
54
|
+
"import wx\n"
|
|
55
|
+
"dlg = wx.MessageDialog(None, 'confirm?', 'confirm?', wx.YES_NO)\n"
|
|
56
|
+
"dlg.ShowModal()\n"
|
|
57
|
+
"dlg.Destroy()\n"
|
|
58
|
+
)
|
|
59
|
+
assert nvda.simulate_modal("enter", timeout=10)
|
|
60
|
+
# end::modal[]
|
|
61
|
+
nvda.wait_until_idle(timeout=15)
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
* xref:tutorial.adoc[Writing Your First Test]
|
|
4
4
|
* Guide
|
|
5
5
|
** xref:guide/fixtures.adoc[Fixtures]
|
|
6
|
+
** xref:guide/dsl.adoc[Writing tests with the DSL]
|
|
6
7
|
** xref:guide/speech.adoc[speech]
|
|
7
8
|
** xref:guide/braille.adoc[braille]
|
|
8
9
|
** xref:guide/keys.adoc[keys]
|
|
9
10
|
** xref:guide/config.adoc[config]
|
|
10
11
|
** xref:guide/log.adoc[log]
|
|
11
12
|
** xref:guide/addons.adoc[addons]
|
|
13
|
+
** xref:guide/modal-dialogs.adoc[Closing a real modal dialog]
|
|
12
14
|
* xref:example-project.adoc[Example Project]
|
|
13
15
|
* xref:ci-guide.adoc[Configuring CI]
|
|
14
16
|
* xref:troubleshooting.adoc[Troubleshooting]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
= Writing tests with the DSL
|
|
2
|
+
|
|
3
|
+
NOTE: The DSL is experimental. Names may change in minor releases until it is marked stable.
|
|
4
|
+
|
|
5
|
+
The `nvda` fixture reads as a list of plain steps. Each step is one sentence on one line. Nothing
|
|
6
|
+
needs nesting, and failure messages are plain numbered text with no colour or tables, so they read
|
|
7
|
+
cleanly through a screen reader.
|
|
8
|
+
|
|
9
|
+
The examples assume the add-on under test is installed. Call `nvda.install_addon()` in the test, or
|
|
10
|
+
request the `addon_under_test` fixture.
|
|
11
|
+
|
|
12
|
+
[source,python]
|
|
13
|
+
----
|
|
14
|
+
def test_the_gesture_announces_the_phrase(nvda):
|
|
15
|
+
include::example$tests_e2e/test_demo_dsl.py[tag=dsl-basic,indent=4]
|
|
16
|
+
----
|
|
17
|
+
|
|
18
|
+
== Actions
|
|
19
|
+
|
|
20
|
+
Actions send input or change NVDA's state. Each one records where speech begins to count for the
|
|
21
|
+
next assertion, so you never pass `since=`.
|
|
22
|
+
|
|
23
|
+
* `nvda.press("NVDA+t")` sends a gesture.
|
|
24
|
+
* `nvda.type("text")` sends one gesture per character. A character that is not a valid gesture
|
|
25
|
+
name raises an error that names it.
|
|
26
|
+
* `nvda.relaunch()` kills and relaunches the NVDA process. The add-on stays installed.
|
|
27
|
+
* `nvda.restart_nvda()` runs NVDA's own restart. Needs `--nvda-allow-eval`.
|
|
28
|
+
|
|
29
|
+
== Assertions
|
|
30
|
+
|
|
31
|
+
* `nvda.should_hear("text")` waits for speech produced after the last action.
|
|
32
|
+
* `nvda.should_hear(matching=r"\d+:\d+")` does the same with a regular expression.
|
|
33
|
+
* `nvda.should_not_hear("error", for_seconds=1)` watches for a fixed time. Put a `should_hear`
|
|
34
|
+
before it, and raise `for_seconds` on slow machines.
|
|
35
|
+
* `nvda.should_log("text")` waits for a log record. It searches the log since the test began, or
|
|
36
|
+
since the last relaunch, because a relaunch starts a new NVDA process with a fresh log. It does
|
|
37
|
+
not restrict the search to records written after the last action.
|
|
38
|
+
* `nvda.should_have_no_errors()` fails on logged errors. Pass `ignoring=[...]` or set
|
|
39
|
+
`ignore-log-errors` to skip runner noise.
|
|
40
|
+
* `nvda.should_have_addon("name", "enabled")` checks an add-on's state.
|
|
41
|
+
|
|
42
|
+
Text matching is plain and case-insensitive, and finds the text anywhere in an utterance. Use
|
|
43
|
+
`matching=` only when you need a regex. A string pattern is searched case-insensitively; a compiled
|
|
44
|
+
pattern keeps its own flags. The wait defaults to 10 seconds. Change it per call with `within=20`,
|
|
45
|
+
or for the project with `timeout = 20` under `[tool.nvda-testkit]`.
|
|
46
|
+
|
|
47
|
+
Two `should_hear` calls after one action each search all of that action's speech. They do not
|
|
48
|
+
check order, and two identical calls pass on a single occurrence.
|
|
49
|
+
|
|
50
|
+
== Waiting for speech around a block
|
|
51
|
+
|
|
52
|
+
[source,python]
|
|
53
|
+
----
|
|
54
|
+
def test_it_speaks_after_the_step(nvda):
|
|
55
|
+
include::example$tests_e2e/test_demo_dsl.py[tag=dsl-expecting,indent=4]
|
|
56
|
+
----
|
|
57
|
+
|
|
58
|
+
If the block relaunches NVDA, the search covers everything the new process said.
|
|
59
|
+
|
|
60
|
+
== Add-on lifecycle
|
|
61
|
+
|
|
62
|
+
Mark the test with `@pytest.mark.fresh_nvda` so it starts from a clean add-on state. The included
|
|
63
|
+
snippet omits the decorator.
|
|
64
|
+
|
|
65
|
+
[source,python]
|
|
66
|
+
----
|
|
67
|
+
def test_install_and_remove(nvda):
|
|
68
|
+
include::example$tests_e2e/test_demo_dsl.py[tag=dsl-lifecycle,indent=4]
|
|
69
|
+
----
|
|
70
|
+
|
|
71
|
+
`install_addon()` installs the bundle from the `addon-bundle` setting, or the path you pass, then
|
|
72
|
+
relaunches NVDA and checks that the add-on is enabled. `remove_addon(name)` removes it, relaunches,
|
|
73
|
+
and checks that it is gone. An add-on installed with `install_addon()` is removed again when the
|
|
74
|
+
test ends.
|
|
75
|
+
|
|
76
|
+
== Dialogs
|
|
77
|
+
|
|
78
|
+
[source,python]
|
|
79
|
+
----
|
|
80
|
+
def test_a_dialog(nvda):
|
|
81
|
+
include::example$tests_e2e/test_demo_dsl.py[tag=dsl-dialog,indent=4]
|
|
82
|
+
----
|
|
83
|
+
|
|
84
|
+
`open_dialog()` and `close_dialog()` are the same steps without the block. While a dialog is open,
|
|
85
|
+
the steps that send input or change state raise an error instead of hanging: `press`, `type`,
|
|
86
|
+
`relaunch`, `restart_nvda`, `restart_harness`, `install_addon`, `remove_addon`, `should_have_addon`,
|
|
87
|
+
`expecting_speech` and `open_dialog`. The assertions that only read speech and log (`should_hear`,
|
|
88
|
+
`should_not_hear`, `should_log` and `should_have_no_errors`) are not guarded and work while a dialog
|
|
89
|
+
is open. The low-level passthroughs `exec`, `eval`, `exec_nowait`, `simulate_modal`,
|
|
90
|
+
`wait_until_idle`, `reset` and `keys.*` are not guarded and still block. Needs
|
|
91
|
+
`--nvda-allow-eval`.
|
|
92
|
+
|
|
93
|
+
A test that ends with a dialog open fails after the kit closes it.
|
|
94
|
+
|
|
95
|
+
== Teardown
|
|
96
|
+
|
|
97
|
+
When a test ends, the `nvda` fixture runs three checks in order. With `fail-on-log-errors`, it
|
|
98
|
+
checks the log first. It then closes a leaked dialog. It then removes the add-ons installed with
|
|
99
|
+
`install_addon()`. Every check runs even if an earlier one fails, and the problems are reported
|
|
100
|
+
together as one teardown error.
|
|
101
|
+
|
|
102
|
+
== Reading failures
|
|
103
|
+
|
|
104
|
+
A failed `should_hear` states what it expected, the action before it, the time waited, and what was
|
|
105
|
+
heard, numbered, one item per line:
|
|
106
|
+
|
|
107
|
+
----
|
|
108
|
+
Expected to hear "PM" within 10 seconds after pressing NVDA+t.
|
|
109
|
+
Time elapsed: 10.05 seconds.
|
|
110
|
+
Heard since that action, 2 items:
|
|
111
|
+
1. "12 colon 00"
|
|
112
|
+
2. "Tuesday"
|
|
113
|
+
Nothing matched. Matching is case-insensitive plain text; use matching= for a regex.
|
|
114
|
+
----
|
|
115
|
+
|
|
116
|
+
Long lists in `should_hear` and `should_log` messages show ten items. `should_have_no_errors` shows
|
|
117
|
+
six unexpected and three ignored records. Run with `--nvda-verbose` to see all of them. For
|
|
118
|
+
terminal output without colour, run `pytest --color=no`.
|
|
119
|
+
|
|
120
|
+
== Before and after
|
|
121
|
+
|
|
122
|
+
Without the DSL:
|
|
123
|
+
|
|
124
|
+
[source,python]
|
|
125
|
+
----
|
|
126
|
+
before = nvda.speech.index()
|
|
127
|
+
nvda.keys.press("NVDA+t")
|
|
128
|
+
found = nvda.speech.wait_for("12:00", timeout=10, since=before)
|
|
129
|
+
assert "12:00" in found.text
|
|
130
|
+
----
|
|
131
|
+
|
|
132
|
+
With it:
|
|
133
|
+
|
|
134
|
+
[source,python]
|
|
135
|
+
----
|
|
136
|
+
nvda.press("NVDA+t")
|
|
137
|
+
nvda.should_hear("12:00")
|
|
138
|
+
----
|
|
139
|
+
|
|
140
|
+
The old API keeps working, and `nvda.speech`, `nvda.keys` and the other namespaces are still there.
|
|
141
|
+
|
|
142
|
+
== Project settings
|
|
143
|
+
|
|
144
|
+
[source,toml]
|
|
145
|
+
----
|
|
146
|
+
[tool.nvda-testkit]
|
|
147
|
+
timeout = 20
|
|
148
|
+
fail-on-log-errors = true
|
|
149
|
+
ignore-log-errors = ["nvwave", "WASAPI"]
|
|
150
|
+
----
|
|
151
|
+
|
|
152
|
+
With `fail-on-log-errors`, every test that uses `nvda` reports a teardown error if NVDA logged an
|
|
153
|
+
unignored error. Each `ignore-log-errors` entry is a regular expression, searched
|
|
154
|
+
case-insensitively.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
= Closing a real modal dialog
|
|
2
|
+
|
|
3
|
+
`nvda.exec()` and `nvda.eval()` both dispatch through NVDA's own main-thread
|
|
4
|
+
queue and wait for the scenario to finish. That works for anything that
|
|
5
|
+
returns on its own, but a real `wx.Dialog.ShowModal()` never drains that
|
|
6
|
+
queue for as long as it's up — confirmed against a real NVDA, not just
|
|
7
|
+
suspected. A scenario that opens one and waits for `exec()` to return will
|
|
8
|
+
sit there until `exec()`'s own timeout fires, with the dialog still open
|
|
9
|
+
afterwards. `nvda.keys.press()` can't reach it either, for the same reason:
|
|
10
|
+
it dispatches through the same queue.
|
|
11
|
+
|
|
12
|
+
The dialog's own message loop is still alive, though — it has to be, to
|
|
13
|
+
receive the click a real user would make. `exec_nowait()` and
|
|
14
|
+
`simulate_modal()` reach it that way instead.
|
|
15
|
+
|
|
16
|
+
[source,python]
|
|
17
|
+
----
|
|
18
|
+
def test_simulate_modal_closes_a_real_dialog(nvda):
|
|
19
|
+
include::example$tests_e2e/test_smoke.py[tag=modal,indent=4]
|
|
20
|
+
----
|
|
21
|
+
|
|
22
|
+
`exec_nowait()` queues a scenario the same way `exec()` does, but returns
|
|
23
|
+
immediately instead of waiting for it to finish — freeing this process's
|
|
24
|
+
single-threaded RPC server to accept the next call while the scenario is
|
|
25
|
+
stuck inside `ShowModal()`. A syntax error in the scenario still raises
|
|
26
|
+
`ScenarioSyntaxError` synchronously, the same as `exec()`; only running the
|
|
27
|
+
scenario is deferred.
|
|
28
|
+
|
|
29
|
+
`simulate_modal(gesture, timeout=10.0)` then runs on that next call's own
|
|
30
|
+
thread, never touching the blocked queue: it polls for our own process to
|
|
31
|
+
take the foreground — what a modal dialog does unconditionally on showing —
|
|
32
|
+
and, once it does, sends `gesture` as real injected keyboard input via
|
|
33
|
+
Win32's `SendInput`, the same path a human's keypress takes. It returns
|
|
34
|
+
`False` on a timeout instead of raising, since that usually means the
|
|
35
|
+
scenario never actually opened a dialog rather than NVDA hanging.
|
|
36
|
+
`gesture` is one of `"enter"`, `"escape"`, `"tab"`, `"space"`, `"yes"`, or
|
|
37
|
+
`"no"`.
|
|
38
|
+
|
|
39
|
+
`exec_nowait()` needs `--nvda-allow-eval`, the same as `eval()`/`exec()`,
|
|
40
|
+
because it runs arbitrary code inside NVDA. `simulate_modal()` does not need
|
|
41
|
+
this flag because it only injects keyboard input.
|
|
42
|
+
|
|
43
|
+
Pair the two: `exec_nowait()` to open the dialog, `simulate_modal()` to
|
|
44
|
+
close it. `exec_nowait()` records which window is in the foreground before it
|
|
45
|
+
queues the scenario, and the next `simulate_modal()` call treats only a
|
|
46
|
+
different window as the dialog. Calling `exec()` instead of `exec_nowait()`
|
|
47
|
+
to open the dialog defeats the point — `simulate_modal()`'s call would never
|
|
48
|
+
even be dispatched.
|
{nvda_addon_testkit-1.0.0 → nvda_addon_testkit-1.1.0}/spy/globalPlugins/nvda_testkit_spy/eval_api.py
RENAMED
|
@@ -11,11 +11,22 @@ exec_in_nvda runs one or more statements and returns whatever the code bound
|
|
|
11
11
|
to a name called __result__, or None if it bound nothing -- multi-statement
|
|
12
12
|
scenarios (e.g. "import core; core.restart()") do not compile under eval()
|
|
13
13
|
and previously needed an unreadable immediately-invoked-lambda workaround.
|
|
14
|
+
|
|
15
|
+
exec_in_nvda_nowait queues a scenario onto the main thread like exec_in_nvda
|
|
16
|
+
does, but does not wait for it to finish before returning. Use it for a
|
|
17
|
+
scenario that opens a real modal dialog: exec_in_nvda would block this
|
|
18
|
+
process's single-threaded RPC server for the dialog's whole lifetime, so a
|
|
19
|
+
paired simulate_modal call (see modal_api.py) could never even be
|
|
20
|
+
dispatched to close it.
|
|
14
21
|
"""
|
|
15
22
|
|
|
16
23
|
import builtins
|
|
17
24
|
|
|
25
|
+
import queueHandler
|
|
26
|
+
from logHandler import log
|
|
27
|
+
|
|
18
28
|
from .mainthread import run_on_main_thread
|
|
29
|
+
from .modal_api import remember_foreground_baseline
|
|
19
30
|
from .registry import rpc_method
|
|
20
31
|
|
|
21
32
|
_SCALARS = (str, int, float, bool, type(None))
|
|
@@ -50,3 +61,21 @@ def _execute(source):
|
|
|
50
61
|
@rpc_method
|
|
51
62
|
def exec_in_nvda(source, timeout=30.0):
|
|
52
63
|
return _marshallable(run_on_main_thread(lambda: _execute(source), timeout=timeout))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@rpc_method
|
|
67
|
+
def exec_in_nvda_nowait(source):
|
|
68
|
+
# Compiled here, synchronously, so a SyntaxError still surfaces on this
|
|
69
|
+
# call the same way exec_in_nvda's does -- only *running* the scenario
|
|
70
|
+
# (which may never return, if it opens a modal dialog) gets queued.
|
|
71
|
+
code = compile(source, "<nvda-testkit>", "exec")
|
|
72
|
+
|
|
73
|
+
def _run():
|
|
74
|
+
try:
|
|
75
|
+
exec(code, {"__builtins__": builtins})
|
|
76
|
+
except Exception:
|
|
77
|
+
log.error("nvda-testkit: exec_in_nvda_nowait scenario raised", exc_info=True)
|
|
78
|
+
|
|
79
|
+
remember_foreground_baseline()
|
|
80
|
+
queueHandler.queueFunction(queueHandler.eventQueue, _run)
|
|
81
|
+
return True
|