@taqwright/taqwright 0.0.24
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.
- package/LICENSE +201 -0
- package/README.md +108 -0
- package/dist/auto-appium.d.ts +12 -0
- package/dist/auto-appium.js +77 -0
- package/dist/bin/branding.d.ts +6 -0
- package/dist/bin/branding.js +22 -0
- package/dist/bin/index.d.ts +2 -0
- package/dist/bin/index.js +321 -0
- package/dist/bin/init.d.ts +26 -0
- package/dist/bin/init.js +902 -0
- package/dist/bin/inspect.d.ts +9 -0
- package/dist/bin/inspect.js +91 -0
- package/dist/bin/report-branding.d.ts +2 -0
- package/dist/bin/report-branding.js +42 -0
- package/dist/branding-assets.d.ts +1 -0
- package/dist/branding-assets.js +1 -0
- package/dist/capabilities-helpers.d.ts +7 -0
- package/dist/capabilities-helpers.js +14 -0
- package/dist/capabilities.d.ts +6 -0
- package/dist/capabilities.js +86 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +235 -0
- package/dist/discovery-setup.d.ts +1 -0
- package/dist/discovery-setup.js +61 -0
- package/dist/discovery.d.ts +17 -0
- package/dist/discovery.js +55 -0
- package/dist/docs/configuration.html +376 -0
- package/dist/docs/custom-reporters.html +265 -0
- package/dist/docs/docker.html +339 -0
- package/dist/docs/docs.js +173 -0
- package/dist/docs/generating-tests.html +161 -0
- package/dist/docs/images/taqwright-html-report.png +0 -0
- package/dist/docs/index.html +13 -0
- package/dist/docs/installation.html +686 -0
- package/dist/docs/parallel.html +271 -0
- package/dist/docs/running-tests.html +385 -0
- package/dist/docs/styles.css +460 -0
- package/dist/docs/writing-tests.html +565 -0
- package/dist/doctor.d.ts +33 -0
- package/dist/doctor.js +508 -0
- package/dist/expect.d.ts +38 -0
- package/dist/expect.js +96 -0
- package/dist/fixture/artifact-mode.d.ts +2 -0
- package/dist/fixture/artifact-mode.js +7 -0
- package/dist/fixture/index.d.ts +15 -0
- package/dist/fixture/index.js +324 -0
- package/dist/images/taqwright-html-report.png +0 -0
- package/dist/images/taqwright_favicon.png +0 -0
- package/dist/images/taqwright_logo.png +0 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +7 -0
- package/dist/inspector/codegen-appium.d.ts +3 -0
- package/dist/inspector/codegen-appium.js +228 -0
- package/dist/inspector/devices.d.ts +41 -0
- package/dist/inspector/devices.js +422 -0
- package/dist/inspector/locator-suggester.d.ts +23 -0
- package/dist/inspector/locator-suggester.js +539 -0
- package/dist/inspector/recorder.d.ts +128 -0
- package/dist/inspector/recorder.js +162 -0
- package/dist/inspector/server.d.ts +39 -0
- package/dist/inspector/server.js +1210 -0
- package/dist/inspector/session.d.ts +84 -0
- package/dist/inspector/session.js +262 -0
- package/dist/inspector/ui.d.ts +1 -0
- package/dist/inspector/ui.js +5508 -0
- package/dist/keys.d.ts +3 -0
- package/dist/keys.js +28 -0
- package/dist/locator/index.d.ts +206 -0
- package/dist/locator/index.js +1506 -0
- package/dist/logger.d.ts +5 -0
- package/dist/logger.js +5 -0
- package/dist/mobile/index.d.ts +130 -0
- package/dist/mobile/index.js +762 -0
- package/dist/network/android.d.ts +5 -0
- package/dist/network/android.js +87 -0
- package/dist/network/ca.d.ts +10 -0
- package/dist/network/ca.js +136 -0
- package/dist/network/har.d.ts +90 -0
- package/dist/network/har.js +101 -0
- package/dist/network/host-proxy.d.ts +16 -0
- package/dist/network/host-proxy.js +134 -0
- package/dist/network/index.d.ts +26 -0
- package/dist/network/index.js +105 -0
- package/dist/network/ios-sim.d.ts +3 -0
- package/dist/network/ios-sim.js +29 -0
- package/dist/network/proxy.d.ts +13 -0
- package/dist/network/proxy.js +310 -0
- package/dist/providers/appium.d.ts +23 -0
- package/dist/providers/appium.js +288 -0
- package/dist/providers/browserstack/index.d.ts +5 -0
- package/dist/providers/browserstack/index.js +77 -0
- package/dist/providers/browserstack/utils.d.ts +1 -0
- package/dist/providers/browserstack/utils.js +6 -0
- package/dist/providers/cloud.d.ts +53 -0
- package/dist/providers/cloud.js +117 -0
- package/dist/providers/emulator/index.d.ts +8 -0
- package/dist/providers/emulator/index.js +47 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.js +33 -0
- package/dist/providers/lambdatest/index.d.ts +28 -0
- package/dist/providers/lambdatest/index.js +99 -0
- package/dist/providers/lambdatest/utils.d.ts +1 -0
- package/dist/providers/lambdatest/utils.js +6 -0
- package/dist/providers/local/index.d.ts +9 -0
- package/dist/providers/local/index.js +53 -0
- package/dist/providers/local-session.d.ts +16 -0
- package/dist/providers/local-session.js +55 -0
- package/dist/setup/archive.d.ts +2 -0
- package/dist/setup/archive.js +43 -0
- package/dist/setup/avd.d.ts +12 -0
- package/dist/setup/avd.js +103 -0
- package/dist/setup/index.d.ts +6 -0
- package/dist/setup/index.js +55 -0
- package/dist/setup/install-android.d.ts +2 -0
- package/dist/setup/install-android.js +70 -0
- package/dist/setup/install-appium.d.ts +1 -0
- package/dist/setup/install-appium.js +64 -0
- package/dist/setup/install-jdk.d.ts +1 -0
- package/dist/setup/install-jdk.js +58 -0
- package/dist/setup/paths.d.ts +16 -0
- package/dist/setup/paths.js +88 -0
- package/dist/setup/spawn-tool.d.ts +3 -0
- package/dist/setup/spawn-tool.js +11 -0
- package/dist/tracer/index.d.ts +34 -0
- package/dist/tracer/index.js +687 -0
- package/dist/tracer/proxy.d.ts +3 -0
- package/dist/tracer/proxy.js +60 -0
- package/dist/types/index.d.ts +189 -0
- package/dist/types/index.js +6 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +37 -0
- package/package.json +79 -0
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Writing tests · taqwright</title>
|
|
7
|
+
<meta name="description" content="Write your first taqwright test. Actions, locators, assertions, isolation, hooks.">
|
|
8
|
+
<link rel="stylesheet" href="styles.css">
|
|
9
|
+
<script>try{var t=localStorage.getItem('tw-theme');if(t==='light'||(!t&&typeof matchMedia==='function'&&matchMedia('(prefers-color-scheme: light)').matches))document.documentElement.setAttribute('data-theme','light');}catch(e){}</script>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
|
|
13
|
+
<header class="topbar">
|
|
14
|
+
<button class="menu-toggle" id="menu-toggle" aria-label="Toggle navigation">☰</button>
|
|
15
|
+
<a class="brand" href="installation.html"><span class="accent">taq</span>wright</a>
|
|
16
|
+
<nav class="primary">
|
|
17
|
+
<a href="installation.html" class="current">Docs</a>
|
|
18
|
+
<a href="generating-tests.html">Inspector</a>
|
|
19
|
+
<a href="writing-tests.html#actions">API</a>
|
|
20
|
+
</nav>
|
|
21
|
+
<span class="spacer"></span>
|
|
22
|
+
<div class="search" title="Search (placeholder)">
|
|
23
|
+
<span>Search docs…</span>
|
|
24
|
+
<span class="kbd">⌘K</span>
|
|
25
|
+
</div>
|
|
26
|
+
<button class="theme-toggle" id="theme-toggle" aria-label="Switch to light theme" title="Switch to light theme"></button>
|
|
27
|
+
<a class="gh-link" href="https://github.com/taqelah/taqwright" title="GitHub">
|
|
28
|
+
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 .5a11.5 11.5 0 0 0-3.64 22.42c.58.1.79-.25.79-.55v-2.02c-3.2.7-3.88-1.36-3.88-1.36-.52-1.33-1.28-1.68-1.28-1.68-1.05-.71.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.55-.29-5.24-1.28-5.24-5.7 0-1.26.45-2.29 1.19-3.1-.12-.29-.52-1.47.11-3.06 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.79 0c2.21-1.49 3.18-1.18 3.18-1.18.63 1.59.23 2.77.11 3.06.74.81 1.18 1.84 1.18 3.1 0 4.43-2.7 5.4-5.27 5.69.41.36.78 1.06.78 2.14v3.17c0 .31.21.66.8.55A11.5 11.5 0 0 0 12 .5Z"/></svg>
|
|
29
|
+
</a>
|
|
30
|
+
</header>
|
|
31
|
+
|
|
32
|
+
<div class="shell">
|
|
33
|
+
|
|
34
|
+
<aside class="sidebar" id="sidebar">
|
|
35
|
+
<h3>Getting started</h3>
|
|
36
|
+
<ul>
|
|
37
|
+
<li><a href="installation.html">Installation</a></li>
|
|
38
|
+
<li><a href="writing-tests.html" class="current">Writing tests</a></li>
|
|
39
|
+
<li class="sub"><a href="writing-tests.html#introduction">Introduction</a></li>
|
|
40
|
+
<li class="sub"><a href="writing-tests.html#first-test">First test</a></li>
|
|
41
|
+
<li class="sub"><a href="writing-tests.html#actions">Actions</a></li>
|
|
42
|
+
<li class="sub"><a href="writing-tests.html#assertions">Assertions</a></li>
|
|
43
|
+
<li class="sub"><a href="writing-tests.html#isolation">Test isolation</a></li>
|
|
44
|
+
<li class="sub"><a href="writing-tests.html#hooks">Hooks</a></li>
|
|
45
|
+
<li><a href="generating-tests.html">Generating tests</a></li>
|
|
46
|
+
<li><a href="running-tests.html">Running & debugging</a></li>
|
|
47
|
+
<li><a href="custom-reporters.html">Custom reporters</a></li>
|
|
48
|
+
<li><a href="parallel.html">Parallel runs</a></li>
|
|
49
|
+
<li><a href="docker.html">Run in Docker</a></li>
|
|
50
|
+
</ul>
|
|
51
|
+
|
|
52
|
+
<h3>Taqwright Test</h3>
|
|
53
|
+
<ul>
|
|
54
|
+
<li><a href="configuration.html">Configuration</a></li>
|
|
55
|
+
</ul>
|
|
56
|
+
|
|
57
|
+
<h3>Reference</h3>
|
|
58
|
+
<ul>
|
|
59
|
+
<li><a href="https://playwright.dev/docs/intro">Playwright runner ↗</a></li>
|
|
60
|
+
<li><a href="https://appium.io/docs/en/latest/">Appium 3 ↗</a></li>
|
|
61
|
+
</ul>
|
|
62
|
+
</aside>
|
|
63
|
+
|
|
64
|
+
<main>
|
|
65
|
+
|
|
66
|
+
<div class="breadcrumb">
|
|
67
|
+
<a href="installation.html">Docs</a> / Getting started / Writing tests
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<h1>Writing tests</h1>
|
|
71
|
+
<p class="lede">
|
|
72
|
+
Taqwright tests <strong>perform actions</strong> on a live mobile device and <strong>assert the resulting UI state</strong>. Each test gets a fresh WebDriver session against your configured emulator, simulator, or physical device. Locators auto-wait for elements to be visible and actionable before driving them, so tests stay reliable even when the app animates.
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
<h2 id="introduction">Introduction</h2>
|
|
76
|
+
<p>
|
|
77
|
+
Taqwright sits on top of <a href="https://playwright.dev/">Playwright</a>'s test runner and <a href="https://appium.io/">Appium 3</a>. From Playwright you get parallelism, retries, projects, sharding, fixtures, the HTML reporter, and the <code>test</code> / <code>expect</code> primitives. From Appium you get UiAutomator2 (Android) and XCUITest (iOS) underneath, with a flat <code>mobile</code> API and a chainable <code>Locator</code> on top.
|
|
78
|
+
</p>
|
|
79
|
+
<p>
|
|
80
|
+
You write tests the same way you'd write a Playwright web test — same file shape, same hooks, same runner flags — but you drive a phone instead of a browser. New here? Do the <a href="installation.html">Installation</a> first.
|
|
81
|
+
</p>
|
|
82
|
+
|
|
83
|
+
<p>You will learn</p>
|
|
84
|
+
<ul>
|
|
85
|
+
<li><a href="#first-test">How to write your first test</a></li>
|
|
86
|
+
<li><a href="#actions">How to perform actions</a> — app lifecycle, locators, per-element</li>
|
|
87
|
+
<li><a href="#assertions">How to use auto-retrying assertions</a></li>
|
|
88
|
+
<li><a href="#isolation">How tests run in isolation</a></li>
|
|
89
|
+
<li><a href="#hooks">How to use test hooks</a></li>
|
|
90
|
+
</ul>
|
|
91
|
+
|
|
92
|
+
<h2 id="first-test">First test</h2>
|
|
93
|
+
<p>
|
|
94
|
+
<code>taqwright init</code> already wrote one for you at <code>tests/example.spec.ts</code> (against the bundled demo app):
|
|
95
|
+
</p>
|
|
96
|
+
|
|
97
|
+
<div class="codeblock">
|
|
98
|
+
<div class="filename">tests/example.spec.ts</div>
|
|
99
|
+
<pre class="code"><button class="copy">Copy</button><code>import { test, expect } from 'taqwright';
|
|
100
|
+
|
|
101
|
+
test('user can log in to the demo app', async ({ mobile }) => {
|
|
102
|
+
await mobile.getByXpath("//*[@hint='Username']").fill('emma@demoapp.com');
|
|
103
|
+
await mobile.getByXpath("//*[@hint='Password']").fill('10203040');
|
|
104
|
+
await mobile.getByUiSelector('new UiSelector().description("Login")').click();
|
|
105
|
+
await expect(mobile.getByUiSelector('new UiSelector().description("View All")')).toBeVisible();
|
|
106
|
+
});</code></pre>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<p>Then run it (Android — see <a href="installation.html#running-example">Running the example test</a>):</p>
|
|
110
|
+
|
|
111
|
+
<div class="codeblock"><pre class="code"><button class="copy">Copy</button><code>npx taqwright test --project=android</code></pre></div>
|
|
112
|
+
|
|
113
|
+
<p>Line-by-line:</p>
|
|
114
|
+
<ul>
|
|
115
|
+
<li><code>import { test, expect } from 'taqwright'</code> — Playwright's <code>test</code> + <code>expect</code>, extended with a <code>mobile</code> fixture. Use them exactly like the Playwright ones you know.</li>
|
|
116
|
+
<li><code>async ({ mobile }) =></code> — destructure the fixture. It opens a fresh WebDriver/Appium session when the test starts and tears it down when it ends.</li>
|
|
117
|
+
<li><code>mobile.getByXpath(…)</code> / <code>getByUiSelector(…)</code> — return a lazy <code>Locator</code>; nothing touches the device until an action or assertion runs. (<code>getByUiSelector</code> is Android-only; <code>getById</code> and the rest are in <a href="#locating">Locating elements</a>.)</li>
|
|
118
|
+
<li><code>.fill('emma@demoapp.com')</code> — the action drives the device: taps the field, clears it, sends the text as real key events (so React Native / Flutter <code>onChangeText</code> handlers fire).</li>
|
|
119
|
+
<li><code>.click()</code> — auto-waits for the element to be visible <strong>and</strong> enabled before tapping. No explicit <code>waitFor</code> needed in the common case.</li>
|
|
120
|
+
<li><code>await expect(locator).toBeVisible()</code> — auto-retrying assertion: polls every 200ms until the element is visible or the timeout (default 30s) elapses.</li>
|
|
121
|
+
</ul>
|
|
122
|
+
|
|
123
|
+
<div class="callout">
|
|
124
|
+
<strong>Two assertion styles, same engine.</strong>
|
|
125
|
+
taqwright ships a Playwright-style <code>expect(locator)</code> wrapper — <code>expect(loc).toBeVisible()</code> / <code>.toHaveText()</code> / <code>.toBeChecked()</code> / … — that delegates to the auto-retrying assertion methods on the <code>Locator</code> itself (<code>loc.assertVisible()</code>, <code>loc.assertText()</code>, …). Both forms behave identically; use whichever reads better. See <a href="#assertions">Assertions</a>.
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<h2 id="actions">Actions</h2>
|
|
129
|
+
<p>
|
|
130
|
+
Actions fall into three buckets: <strong>app lifecycle</strong> (install / launch / background / deep-link), <strong>locating</strong> (the <code>getBy*</code> family), and <strong>per-element actions</strong> on the returned <code>Locator</code>.
|
|
131
|
+
</p>
|
|
132
|
+
|
|
133
|
+
<h3 id="app-lifecycle">App lifecycle</h3>
|
|
134
|
+
<p>
|
|
135
|
+
Most tests don't need to call these directly — the fixture handles install + launch via <code>resetBetweenTests</code> (see <a href="#isolation">Test isolation</a>). But they're there when you need them:
|
|
136
|
+
</p>
|
|
137
|
+
|
|
138
|
+
<div class="codeblock"><pre class="code"><button class="copy">Copy</button><code>// Install and launch
|
|
139
|
+
await mobile.installApp('/path/to/app.apk');
|
|
140
|
+
await mobile.launchApp('com.example.app');
|
|
141
|
+
|
|
142
|
+
// Send to background for 5 seconds, then come back
|
|
143
|
+
await mobile.backgroundApp(5);
|
|
144
|
+
await mobile.activateApp('com.example.app');
|
|
145
|
+
|
|
146
|
+
// Deep link
|
|
147
|
+
await mobile.openDeepLink('myapp://product/123');
|
|
148
|
+
|
|
149
|
+
// Android-only system back
|
|
150
|
+
await mobile.goBack();
|
|
151
|
+
|
|
152
|
+
// Tear down
|
|
153
|
+
await mobile.terminateApp('com.example.app');</code></pre></div>
|
|
154
|
+
|
|
155
|
+
<h3 id="locating">Locating elements</h3>
|
|
156
|
+
<p>
|
|
157
|
+
Pick the locator strategy that matches what your app exposes. Most stable to least:
|
|
158
|
+
</p>
|
|
159
|
+
|
|
160
|
+
<table class="api">
|
|
161
|
+
<thead><tr><th>Method</th><th>What it matches</th></tr></thead>
|
|
162
|
+
<tbody>
|
|
163
|
+
<tr><td><code>getById('login_btn')</code></td><td>Accessibility id (iOS <code>name</code>) / Android <code>resource-id</code>. Same as <code>getByTestId</code>.</td></tr>
|
|
164
|
+
<tr><td><code>getByLabel('Submit')</code></td><td>Accessibility label.</td></tr>
|
|
165
|
+
<tr><td><code>getByText('Submit')</code></td><td>Visible text. Pass <code>{ exact: false }</code> for substring, or a <code>RegExp</code> for regex.</td></tr>
|
|
166
|
+
<tr><td><code>getByPlaceholder('Email')</code></td><td>Input placeholder / hint text.</td></tr>
|
|
167
|
+
<tr><td><code>getByRole('button', { name })</code></td><td>Best-effort role → widget-type mapping.</td></tr>
|
|
168
|
+
<tr><td><code>getByType('android.widget.Button')</code></td><td>Class name / XCUI element type. Use for grouping (e.g. <code>.all()</code>).</td></tr>
|
|
169
|
+
<tr><td><code>getByXpath('//*[@hint="..."]')</code></td><td>Raw XPath. Slowest; reach for last.</td></tr>
|
|
170
|
+
<tr><td><code>getByUiSelector('new UiSelector()...')</code></td><td>Android UiAutomator2 selector. Fast, expressive.</td></tr>
|
|
171
|
+
<tr><td><code>getByPredicate("type == '...'")</code></td><td>iOS NSPredicate. Fastest iOS strategy.</td></tr>
|
|
172
|
+
<tr><td><code>getByClassChain('**/X[`label == "OK"`]')</code></td><td>iOS class-chain. Hierarchical, much faster than XPath.</td></tr>
|
|
173
|
+
</tbody>
|
|
174
|
+
</table>
|
|
175
|
+
|
|
176
|
+
<p>
|
|
177
|
+
When several elements match the same locator, <strong>chain</strong> to disambiguate:
|
|
178
|
+
</p>
|
|
179
|
+
|
|
180
|
+
<div class="codeblock"><pre class="code"><button class="copy">Copy</button><code>// Pick by position
|
|
181
|
+
await mobile.getByText('Item').nth(2).click();
|
|
182
|
+
await mobile.getByText('Item').first().click();
|
|
183
|
+
await mobile.getByText('Item').last().click();
|
|
184
|
+
|
|
185
|
+
// Filter
|
|
186
|
+
await mobile.getByType('android.widget.LinearLayout')
|
|
187
|
+
.filter({ hasText: 'Wi-Fi' })
|
|
188
|
+
.click();
|
|
189
|
+
|
|
190
|
+
// Scope a child find under a parent
|
|
191
|
+
await mobile.getById('login_form')
|
|
192
|
+
.locator(mobile.getById('submit'))
|
|
193
|
+
.click();
|
|
194
|
+
|
|
195
|
+
// Iterate
|
|
196
|
+
for (const row of await mobile.getByType('XCUIElementTypeCell').all()) {
|
|
197
|
+
console.log(await row.getText());
|
|
198
|
+
}</code></pre></div>
|
|
199
|
+
|
|
200
|
+
<h3 id="common-actions">Common actions</h3>
|
|
201
|
+
<p>
|
|
202
|
+
Here are the most popular per-element actions, called on the <code>Locator</code> a <code>getBy*</code> returns. Each auto-waits for actionability — visible, enabled, stable — so you never write a manual wait:
|
|
203
|
+
</p>
|
|
204
|
+
|
|
205
|
+
<table class="api">
|
|
206
|
+
<thead><tr><th>Action</th><th>Description</th></tr></thead>
|
|
207
|
+
<tbody>
|
|
208
|
+
<tr><td><code>click()</code> / <code>tap()</code></td><td>Tap the element. Auto-waits for visible + enabled.</td></tr>
|
|
209
|
+
<tr><td><code>fill(text)</code></td><td>Focus, clear, send the text as real key events.</td></tr>
|
|
210
|
+
<tr><td><code>clear()</code></td><td>Empty the field.</td></tr>
|
|
211
|
+
<tr><td><code>check()</code> / <code>uncheck()</code></td><td>Idempotent toggle for switches and checkboxes.</td></tr>
|
|
212
|
+
<tr><td><code>focus()</code> / <code>blur()</code></td><td>Bring focus (taps the field) / dismiss the keyboard.</td></tr>
|
|
213
|
+
<tr><td><code>press('Enter')</code></td><td>Single key. Supports <code>Enter</code> / <code>Tab</code> / <code>Backspace</code> / <code>Space</code> / arrows / nav keys.</td></tr>
|
|
214
|
+
<tr><td><code>pressSequentially(text, { delay })</code></td><td>One char at a time. Use when autocomplete needs to react.</td></tr>
|
|
215
|
+
<tr><td><code>selectOption(value)</code></td><td>Drive a native picker / spinner / date / time picker.</td></tr>
|
|
216
|
+
<tr><td><code>scrollIntoView()</code></td><td>Native scroll-to-visible if available, gesture-swipe fallback.</td></tr>
|
|
217
|
+
<tr><td><code>swipeLeft()</code> / <code>Right</code> / <code>Up</code> / <code>Down</code></td><td>Swipe inside the element's bounding box.</td></tr>
|
|
218
|
+
<tr><td><code>longPress({ duration })</code></td><td>Press-and-hold. Default 1000ms.</td></tr>
|
|
219
|
+
<tr><td><code>dragTo(target)</code></td><td>Drag onto another locator.</td></tr>
|
|
220
|
+
<tr><td><code>pinchIn()</code> / <code>pinchOut()</code></td><td>Two-finger zoom.</td></tr>
|
|
221
|
+
<tr><td><code>screenshot()</code></td><td>Element screenshot as a <code>Buffer</code>.</td></tr>
|
|
222
|
+
</tbody>
|
|
223
|
+
</table>
|
|
224
|
+
|
|
225
|
+
<h3 id="mobile-specific">Mobile-specific actions</h3>
|
|
226
|
+
<p>
|
|
227
|
+
These have no direct web Playwright analogue:
|
|
228
|
+
</p>
|
|
229
|
+
|
|
230
|
+
<div class="codeblock"><pre class="code"><button class="copy">Copy</button><code>// Hardware buttons — HOME | BACK | POWER | VOLUME_UP | VOLUME_DOWN | ENTER
|
|
231
|
+
await mobile.pressButton('BACK');
|
|
232
|
+
await mobile.pressButton('HOME');
|
|
233
|
+
await mobile.pressButton('VOLUME_UP');
|
|
234
|
+
await mobile.press('Enter'); // a single named key, device-level
|
|
235
|
+
await mobile.goBack(); // Android back / iOS nav-bar back
|
|
236
|
+
|
|
237
|
+
// Screen-level gestures (vs the locator-scoped ones in Common actions)
|
|
238
|
+
await mobile.swipe('left');
|
|
239
|
+
await mobile.scroll('down'); // content-reveal direction
|
|
240
|
+
await mobile.scroll('down', { from: { y: 0.7 }, to: { y: 0.3 } });
|
|
241
|
+
await mobile.scrollIntoView(mobile.getByText('Submit'));
|
|
242
|
+
await mobile.dragAndDrop({ x: 100, y: 400 }, { x: 100, y: 120 });
|
|
243
|
+
await mobile.clickByPercent({ x: 0, y: 0, width: 1080, height: 2400 }, 0.5, 0.9);
|
|
244
|
+
await mobile.gesture({ pointers: [/* multi-finger W3C pointer path */] });
|
|
245
|
+
|
|
246
|
+
// Pickers — auto-detects element type
|
|
247
|
+
await mobile.getByType('XCUIElementTypePickerWheel').selectOption('March');
|
|
248
|
+
await mobile.getByType('XCUIElementTypeDatePicker').selectOption({ date: '2026-05-14' });
|
|
249
|
+
await mobile.getByType('android.widget.Spinner').selectOption('Large');
|
|
250
|
+
|
|
251
|
+
// Deep links / orientation / keyboard
|
|
252
|
+
await mobile.openDeepLink('myapp://settings');
|
|
253
|
+
await mobile.setOrientation('landscape');
|
|
254
|
+
const o = await mobile.getOrientation(); // 'portrait' | 'landscape'
|
|
255
|
+
await mobile.hideKeyboard();
|
|
256
|
+
const up = await mobile.isKeyboardShown();
|
|
257
|
+
|
|
258
|
+
// OS dialogs
|
|
259
|
+
await mobile.acceptAlert();
|
|
260
|
+
await mobile.dismissAlert();
|
|
261
|
+
const msg = await mobile.getAlertText();
|
|
262
|
+
|
|
263
|
+
// App state & lifecycle queries
|
|
264
|
+
await mobile.isAppInstalled(); // defaults to the configured bundle id
|
|
265
|
+
await mobile.queryAppState(); // not_installed | not_running | background | foreground
|
|
266
|
+
const { bundleId } = await mobile.getCurrentApp();
|
|
267
|
+
await mobile.backgroundApp(3); // send to background for 3s (-1 = indefinitely)
|
|
268
|
+
|
|
269
|
+
// Clipboard / geolocation
|
|
270
|
+
await mobile.setClipboard('hello');
|
|
271
|
+
const clip = await mobile.getClipboard();
|
|
272
|
+
await mobile.setLocation({ latitude: 37.422, longitude: -122.084 });
|
|
273
|
+
const loc = await mobile.getLocation();
|
|
274
|
+
|
|
275
|
+
// Permissions / network (Android-only — throw a clear error on iOS)
|
|
276
|
+
await mobile.setPermission('android.permission.CAMERA', 'grant');
|
|
277
|
+
await mobile.setNetworkConnection({ wifi: false, data: true, airplane: false });
|
|
278
|
+
const net = await mobile.getNetworkConnection();
|
|
279
|
+
|
|
280
|
+
// Files / logs / locale / time
|
|
281
|
+
await mobile.pushFile('/sdcard/Download/in.txt', 'payload');
|
|
282
|
+
const buf = await mobile.pullFile('/sdcard/Download/out.txt');
|
|
283
|
+
const logs = await mobile.getDeviceLogs(); // 'logcat' | 'syslog' | 'crashlog'
|
|
284
|
+
const types = await mobile.getLogTypes();
|
|
285
|
+
await mobile.setLocale('en-US'); // Android-only at runtime
|
|
286
|
+
const time = await mobile.getDeviceTime();
|
|
287
|
+
|
|
288
|
+
// Screen recording (separate from the auto-captured trace video)
|
|
289
|
+
await mobile.startScreenRecording();
|
|
290
|
+
const mp4 = await mobile.stopScreenRecording();
|
|
291
|
+
|
|
292
|
+
// Device info / misc
|
|
293
|
+
const size = await mobile.getScreenSize(); // { width, height }
|
|
294
|
+
const png = await mobile.screenshot(); // full-screen Buffer
|
|
295
|
+
const xml = await mobile.viewTree(); // current page source
|
|
296
|
+
await mobile.waitForTimeout(500);
|
|
297
|
+
await mobile.pause(); // attach the inspector mid-test (no-op if PWDEBUG=0)</code></pre></div>
|
|
298
|
+
|
|
299
|
+
<p>
|
|
300
|
+
<code>mobile.raw</code> is the escape hatch back to the underlying WebDriver
|
|
301
|
+
<code>Client</code> if you need a command not surfaced here.
|
|
302
|
+
</p>
|
|
303
|
+
|
|
304
|
+
<h2 id="assertions">Assertions</h2>
|
|
305
|
+
|
|
306
|
+
<h3 id="locator-assertions">Locator assertions (auto-retrying)</h3>
|
|
307
|
+
<p>
|
|
308
|
+
Every assertion that operates on a <code>Locator</code> is a method on the locator itself, not a <code>expect</code> matcher. They auto-retry — polling every 200ms until the condition holds or the timeout elapses.
|
|
309
|
+
</p>
|
|
310
|
+
|
|
311
|
+
<div class="codeblock"><pre class="code"><button class="copy">Copy</button><code>const submit = mobile.getById('Submit');
|
|
312
|
+
|
|
313
|
+
await submit.assertVisible();
|
|
314
|
+
await submit.assertEnabled();
|
|
315
|
+
await submit.assertText('Submit order');
|
|
316
|
+
await submit.assertContainsText('Submit');
|
|
317
|
+
|
|
318
|
+
// State for toggles
|
|
319
|
+
await mobile.getById('darkMode').assertChecked();
|
|
320
|
+
|
|
321
|
+
// Counts (pairs with .all() / chain)
|
|
322
|
+
await mobile.getByType('CartItem').assertCount(3);
|
|
323
|
+
|
|
324
|
+
// Arbitrary attribute
|
|
325
|
+
await mobile.getById('promo').assertAttribute('content-desc', /Save \d+%/);
|
|
326
|
+
|
|
327
|
+
// Position / structure
|
|
328
|
+
await mobile.getByText('Footer').assertInViewport();
|
|
329
|
+
await mobile.getById('cart_count').assertEmpty();
|
|
330
|
+
await mobile.getById('username').assertFocused();</code></pre></div>
|
|
331
|
+
|
|
332
|
+
<table class="api">
|
|
333
|
+
<thead><tr><th>Method</th><th>Asserts</th></tr></thead>
|
|
334
|
+
<tbody>
|
|
335
|
+
<tr><td><code>assertVisible()</code> / <code>assertHidden()</code></td><td>Element is (or isn't) displayed.</td></tr>
|
|
336
|
+
<tr><td><code>assertEnabled()</code> / <code>assertDisabled()</code></td><td>Element is interactive.</td></tr>
|
|
337
|
+
<tr><td><code>assertChecked()</code> / <code>assertUnchecked()</code></td><td>Toggle / switch state.</td></tr>
|
|
338
|
+
<tr><td><code>assertText(s | RegExp)</code></td><td>Exact text or regex match.</td></tr>
|
|
339
|
+
<tr><td><code>assertContainsText(s)</code></td><td>Substring match.</td></tr>
|
|
340
|
+
<tr><td><code>assertValue(s | RegExp)</code></td><td>Input value match.</td></tr>
|
|
341
|
+
<tr><td><code>assertCount(n)</code></td><td>The chain currently matches exactly <code>n</code> elements.</td></tr>
|
|
342
|
+
<tr><td><code>assertAttribute(name, s | RegExp)</code></td><td>Arbitrary attribute matches.</td></tr>
|
|
343
|
+
<tr><td><code>assertEditable()</code> / <code>assertReadonly()</code></td><td>Input is (or isn't) editable.</td></tr>
|
|
344
|
+
<tr><td><code>assertFocused()</code></td><td>Element has keyboard focus.</td></tr>
|
|
345
|
+
<tr><td><code>assertAttached()</code></td><td>Element exists in the UI tree (whether or not visible).</td></tr>
|
|
346
|
+
<tr><td><code>assertEmpty()</code></td><td>No children and no text.</td></tr>
|
|
347
|
+
<tr><td><code>assertInViewport()</code></td><td>Bounding box intersects the device viewport.</td></tr>
|
|
348
|
+
</tbody>
|
|
349
|
+
</table>
|
|
350
|
+
|
|
351
|
+
<h3 id="generic-matchers">Generic matchers (<code>expect</code>)</h3>
|
|
352
|
+
<p>
|
|
353
|
+
Playwright's <code>expect</code> is re-exported from <code>taqwright</code>. For <strong>plain values</strong> — strings, numbers, arrays, objects — it's Playwright's <code>expect</code> unchanged. For a <code>Locator</code> it returns taqwright's auto-retrying mobile matchers (next section).
|
|
354
|
+
</p>
|
|
355
|
+
|
|
356
|
+
<div class="codeblock"><pre class="code"><button class="copy">Copy</button><code>import { test, expect } from 'taqwright';
|
|
357
|
+
|
|
358
|
+
test('totals match', async ({ mobile }) => {
|
|
359
|
+
const total = await mobile.getById('total').getText();
|
|
360
|
+
expect(total).toBe('$69.99');
|
|
361
|
+
|
|
362
|
+
const items = await mobile.getByType('CartItem').all();
|
|
363
|
+
expect(items).toHaveLength(3);
|
|
364
|
+
|
|
365
|
+
const isPromoApplied = await mobile.getById('promo_active').isVisible();
|
|
366
|
+
expect(isPromoApplied).toBeTruthy();
|
|
367
|
+
});</code></pre></div>
|
|
368
|
+
|
|
369
|
+
<h3 id="expect-extend">Using <code>expect(locator)</code></h3>
|
|
370
|
+
<p>
|
|
371
|
+
No setup, no <code>expect.extend</code>. <code>expect</code> imported from <code>taqwright</code> returns taqwright's mobile matchers when you pass a <code>Locator</code>, and falls through to Playwright's real <code>expect</code> for plain values. Each locator matcher just delegates to the matching auto-retrying <a href="#locator-assertions"><code>assert*</code></a> method:
|
|
372
|
+
</p>
|
|
373
|
+
|
|
374
|
+
<div class="codeblock"><pre class="code"><button class="copy">Copy</button><code>import { test, expect } from 'taqwright';
|
|
375
|
+
|
|
376
|
+
test('cart', async ({ mobile }) => {
|
|
377
|
+
await expect(mobile.getById('VIEW CART')).toBeVisible();
|
|
378
|
+
await expect(mobile.getByText('Total')).toContainText('$69.99');
|
|
379
|
+
await expect(mobile.getByType('CartItem')).toHaveCount(3);
|
|
380
|
+
await expect(mobile.getById('darkMode')).toBeChecked();
|
|
381
|
+
|
|
382
|
+
// plain values still use Playwright's expect, unchanged
|
|
383
|
+
const total = await mobile.getById('total').getText();
|
|
384
|
+
expect(total).toBe('$69.99');
|
|
385
|
+
});</code></pre></div>
|
|
386
|
+
|
|
387
|
+
<p>
|
|
388
|
+
Here are the most popular locator matchers. Each maps 1:1 to the auto-retrying <a href="#locator-assertions"><code>assert*</code></a> method of the same meaning:
|
|
389
|
+
</p>
|
|
390
|
+
|
|
391
|
+
<table class="api">
|
|
392
|
+
<thead><tr><th>Assertion</th><th>Description</th></tr></thead>
|
|
393
|
+
<tbody>
|
|
394
|
+
<tr><td><code>expect(locator).toBeVisible()</code></td><td>Element is visible</td></tr>
|
|
395
|
+
<tr><td><code>expect(locator).toBeHidden()</code></td><td>Element is hidden / not displayed</td></tr>
|
|
396
|
+
<tr><td><code>expect(locator).toBeEnabled()</code></td><td>Control is enabled</td></tr>
|
|
397
|
+
<tr><td><code>expect(locator).toBeDisabled()</code></td><td>Control is disabled</td></tr>
|
|
398
|
+
<tr><td><code>expect(locator).toBeChecked()</code></td><td>Checkbox / switch is checked</td></tr>
|
|
399
|
+
<tr><td><code>expect(locator).toBeEditable()</code></td><td>Input is editable</td></tr>
|
|
400
|
+
<tr><td><code>expect(locator).toBeFocused()</code></td><td>Element has keyboard focus</td></tr>
|
|
401
|
+
<tr><td><code>expect(locator).toBeAttached()</code></td><td>Element exists in the UI tree</td></tr>
|
|
402
|
+
<tr><td><code>expect(locator).toBeInViewport()</code></td><td>Element intersects the device viewport</td></tr>
|
|
403
|
+
<tr><td><code>expect(locator).toBeEmpty()</code></td><td>Element has no children and no text</td></tr>
|
|
404
|
+
<tr><td><code>expect(locator).toHaveText(s | RegExp)</code></td><td>Element matches text</td></tr>
|
|
405
|
+
<tr><td><code>expect(locator).toContainText(s)</code></td><td>Element contains text</td></tr>
|
|
406
|
+
<tr><td><code>expect(locator).toHaveValue(s | RegExp)</code></td><td>Input element has value</td></tr>
|
|
407
|
+
<tr><td><code>expect(locator).toHaveCount(n)</code></td><td>The chain matches exactly <code>n</code> elements</td></tr>
|
|
408
|
+
<tr><td><code>expect(locator).toHaveAttribute(name, s | RegExp)</code></td><td>Element has attribute</td></tr>
|
|
409
|
+
</tbody>
|
|
410
|
+
</table>
|
|
411
|
+
|
|
412
|
+
<p>
|
|
413
|
+
The paired matchers also take a <code>.not</code> form — <code>.not.toBeVisible()</code>, <code>.not.toBeEnabled()</code>, <code>.not.toBeChecked()</code>, <code>.not.toBeEditable()</code>. Web-only Playwright matchers (<code>toHaveScreenshot</code>, <code>toHaveCSS</code>, <code>toHaveTitle</code>, …) throw a clear error rather than misfire. It's a thin standalone wrapper (<em>not</em> <code>expect.extend</code>), so Playwright's value matchers, <code>expect.soft</code>, <code>expect.poll</code> and <code>expect.configure</code> are all unchanged.
|
|
414
|
+
</p>
|
|
415
|
+
|
|
416
|
+
<p>
|
|
417
|
+
To poll an arbitrary predicate (not a <code>Locator</code>), <code>expect.poll</code> works exactly as in Playwright:
|
|
418
|
+
</p>
|
|
419
|
+
|
|
420
|
+
<div class="codeblock"><pre class="code"><button class="copy">Copy</button><code>await expect
|
|
421
|
+
.poll(() => mobile.getById('VIEW CART').isVisible(), { timeout: 10_000 })
|
|
422
|
+
.toBe(true);</code></pre></div>
|
|
423
|
+
|
|
424
|
+
<h2 id="isolation">Test isolation</h2>
|
|
425
|
+
<p>
|
|
426
|
+
Each worker holds one WebDriver / Appium session. By default that session stays open across tests in the same worker — fast, but tests can leak state.
|
|
427
|
+
</p>
|
|
428
|
+
<p>
|
|
429
|
+
Set <code>resetBetweenTests: true</code> in your config to get a clean app between tests:
|
|
430
|
+
</p>
|
|
431
|
+
|
|
432
|
+
<div class="codeblock">
|
|
433
|
+
<div class="filename">taqwright.config.ts</div>
|
|
434
|
+
<pre class="code"><button class="copy">Copy</button><code>import { defineConfig, Platform } from 'taqwright';
|
|
435
|
+
|
|
436
|
+
export default defineConfig({
|
|
437
|
+
projects: [{
|
|
438
|
+
name: 'android-emulator',
|
|
439
|
+
use: {
|
|
440
|
+
platform: Platform.ANDROID,
|
|
441
|
+
device: { provider: 'emulator', name: 'Pixel_7_API_34' },
|
|
442
|
+
buildPath: './app/build/outputs/apk/debug/app-debug.apk',
|
|
443
|
+
appBundleId: 'com.example.app',
|
|
444
|
+
resetBetweenTests: true,
|
|
445
|
+
},
|
|
446
|
+
}],
|
|
447
|
+
});</code></pre>
|
|
448
|
+
</div>
|
|
449
|
+
|
|
450
|
+
<p>
|
|
451
|
+
When <code>resetBetweenTests</code> is <code>true</code>, the <code>mobile</code> fixture <strong>terminates → uninstalls → re-installs → activates</strong> the app between every test. Both <code>buildPath</code> and <code>appBundleId</code> are required — TypeScript will enforce that.
|
|
452
|
+
</p>
|
|
453
|
+
|
|
454
|
+
<h2 id="hooks">Hooks</h2>
|
|
455
|
+
<p>
|
|
456
|
+
Inherited from Playwright unchanged: <code>test.describe</code>, <code>test.beforeEach</code>, <code>test.afterEach</code>, <code>test.beforeAll</code>, <code>test.afterAll</code>.
|
|
457
|
+
</p>
|
|
458
|
+
|
|
459
|
+
<div class="codeblock">
|
|
460
|
+
<div class="filename">tests/cart.spec.ts</div>
|
|
461
|
+
<pre class="code"><button class="copy">Copy</button><code>import { test } from 'taqwright';
|
|
462
|
+
|
|
463
|
+
test.describe('Cart', () => {
|
|
464
|
+
test.beforeEach(async ({ mobile }) => {
|
|
465
|
+
await mobile.getById('Username').fill('emma@demoapp.com');
|
|
466
|
+
await mobile.getById('Password').fill('10203040');
|
|
467
|
+
await mobile.getById('Login').click();
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
test('add to cart', async ({ mobile }) => {
|
|
471
|
+
await mobile.getById('View All').click();
|
|
472
|
+
await mobile.getById('Search dresses...').fill('boho');
|
|
473
|
+
await mobile.getById('Boho Wrap Dress\n$69.99').click();
|
|
474
|
+
await mobile.getById('Add to Cart').click();
|
|
475
|
+
await mobile.getById('VIEW CART').assertVisible();
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
test('remove from cart', async ({ mobile }) => {
|
|
479
|
+
/* ... */
|
|
480
|
+
});
|
|
481
|
+
});</code></pre>
|
|
482
|
+
</div>
|
|
483
|
+
|
|
484
|
+
<div class="callout">
|
|
485
|
+
<strong>Tip — keep <code>beforeEach</code> light.</strong>
|
|
486
|
+
Every action there runs before every test. If your login flow takes 8s, three tests cost 24s of repeated work. Combine with <code>resetBetweenTests: false</code> (default) and a single <code>test.beforeAll</code> if the tests don't need fresh app state.
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<div class="pager">
|
|
490
|
+
<a href="installation.html">
|
|
491
|
+
<div class="label">← Previous</div>
|
|
492
|
+
<div class="title">Installation</div>
|
|
493
|
+
</a>
|
|
494
|
+
<a class="next" href="generating-tests.html">
|
|
495
|
+
<div class="label">Next →</div>
|
|
496
|
+
<div class="title">Generating tests</div>
|
|
497
|
+
</a>
|
|
498
|
+
</div>
|
|
499
|
+
|
|
500
|
+
</main>
|
|
501
|
+
|
|
502
|
+
<aside class="onthispage">
|
|
503
|
+
<h4>On this page</h4>
|
|
504
|
+
<ol>
|
|
505
|
+
<li><a href="#introduction">Introduction</a></li>
|
|
506
|
+
<li><a href="#first-test">First test</a></li>
|
|
507
|
+
<li><a href="#actions">Actions</a>
|
|
508
|
+
<ol class="sub">
|
|
509
|
+
<li><a href="#app-lifecycle">App lifecycle</a></li>
|
|
510
|
+
<li><a href="#locating">Locating elements</a></li>
|
|
511
|
+
<li><a href="#common-actions">Common actions</a></li>
|
|
512
|
+
<li><a href="#mobile-specific">Mobile-specific</a></li>
|
|
513
|
+
</ol>
|
|
514
|
+
</li>
|
|
515
|
+
<li><a href="#assertions">Assertions</a>
|
|
516
|
+
<ol class="sub">
|
|
517
|
+
<li><a href="#locator-assertions">Locator assertions</a></li>
|
|
518
|
+
<li><a href="#generic-matchers">Generic matchers</a></li>
|
|
519
|
+
<li><a href="#expect-extend">Using <code>expect</code></a></li>
|
|
520
|
+
</ol>
|
|
521
|
+
</li>
|
|
522
|
+
<li><a href="#isolation">Test isolation</a></li>
|
|
523
|
+
<li><a href="#hooks">Hooks</a></li>
|
|
524
|
+
</ol>
|
|
525
|
+
</aside>
|
|
526
|
+
|
|
527
|
+
</div>
|
|
528
|
+
|
|
529
|
+
<footer class="bot">
|
|
530
|
+
<div class="cols">
|
|
531
|
+
<div>
|
|
532
|
+
<h5>Getting started</h5>
|
|
533
|
+
<ul>
|
|
534
|
+
<li><a href="installation.html">Installation</a></li>
|
|
535
|
+
<li><a href="writing-tests.html">Writing tests</a></li>
|
|
536
|
+
<li><a href="generating-tests.html">Generating tests</a></li>
|
|
537
|
+
<li><a href="running-tests.html">Running & debugging</a></li>
|
|
538
|
+
</ul>
|
|
539
|
+
</div>
|
|
540
|
+
<div>
|
|
541
|
+
<h5>Guides</h5>
|
|
542
|
+
<ul>
|
|
543
|
+
<li><a href="custom-reporters.html">Custom reporters</a></li>
|
|
544
|
+
<li><a href="parallel.html">Parallel runs</a></li>
|
|
545
|
+
<li><a href="docker.html">Run in Docker</a></li>
|
|
546
|
+
<li><a href="writing-tests.html#actions">API surface</a></li>
|
|
547
|
+
</ul>
|
|
548
|
+
</div>
|
|
549
|
+
<div>
|
|
550
|
+
<h5>Built on</h5>
|
|
551
|
+
<ul>
|
|
552
|
+
<li><a href="https://playwright.dev/">Playwright</a></li>
|
|
553
|
+
<li><a href="https://appium.io/">Appium 3</a></li>
|
|
554
|
+
</ul>
|
|
555
|
+
</div>
|
|
556
|
+
</div>
|
|
557
|
+
<div class="copy">
|
|
558
|
+
Apache-2.0 licensed · Built on Playwright + Appium
|
|
559
|
+
</div>
|
|
560
|
+
</footer>
|
|
561
|
+
|
|
562
|
+
<script src="docs.js"></script>
|
|
563
|
+
|
|
564
|
+
</body>
|
|
565
|
+
</html>
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export { normalizeSysImagePath } from './setup/avd.js';
|
|
2
|
+
export type DoctorStatus = 'ok' | 'warn' | 'error';
|
|
3
|
+
export interface DoctorCheck {
|
|
4
|
+
name: string;
|
|
5
|
+
status: DoctorStatus;
|
|
6
|
+
detail?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function runDoctorChecks(): Promise<DoctorCheck[]>;
|
|
9
|
+
export type AppiumSupportLevel = 'recommended' | 'best-effort' | 'unsupported';
|
|
10
|
+
export declare function classifyAppiumVersion(version: string): AppiumSupportLevel;
|
|
11
|
+
export type JdkLevel = 'ok' | 'too-old' | 'unknown';
|
|
12
|
+
export declare function classifyJdkVersion(version: string): JdkLevel;
|
|
13
|
+
export interface AndroidToolchainStatus {
|
|
14
|
+
jdk: JdkLevel | 'missing';
|
|
15
|
+
jdkVersion?: string;
|
|
16
|
+
sdk: boolean;
|
|
17
|
+
appium: AppiumSupportLevel | 'missing';
|
|
18
|
+
appiumVersion?: string;
|
|
19
|
+
uiautomator2: boolean;
|
|
20
|
+
avd: boolean;
|
|
21
|
+
avdNames: string[];
|
|
22
|
+
ready: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function androidToolchainReady(s: {
|
|
25
|
+
jdk: JdkLevel | 'missing';
|
|
26
|
+
sdk: boolean;
|
|
27
|
+
appium: AppiumSupportLevel | 'missing';
|
|
28
|
+
uiautomator2: boolean;
|
|
29
|
+
}): boolean;
|
|
30
|
+
export declare function listAvds(): Promise<string[]>;
|
|
31
|
+
export declare function detectAndroidToolchain(): Promise<AndroidToolchainStatus>;
|
|
32
|
+
export declare function isAppiumVersionSupported(version: string): boolean;
|
|
33
|
+
export declare function isNodeVersionSupported(version: string): boolean;
|