@taqwright/taqwright 0.0.24 → 0.0.26
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/README.md +2 -2
- package/dist/bin/init.js +4 -10
- package/dist/capabilities.js +3 -0
- package/dist/inspector/ui.d.ts +1 -1
- package/dist/inspector/ui.js +77 -3
- package/package.json +2 -5
package/dist/inspector/ui.js
CHANGED
|
@@ -431,6 +431,16 @@ export const INSPECTOR_HTML = `<!doctype html>
|
|
|
431
431
|
background: #000; box-shadow: 0 6px 22px rgba(0,0,0,0.10); }
|
|
432
432
|
#screen-img { display: block; max-width: 100%; max-height: calc(100vh - 100px);
|
|
433
433
|
user-select: none; -webkit-user-drag: none; }
|
|
434
|
+
/* Graceful fallback when a snapshot fails / returns no screenshot — shown
|
|
435
|
+
instead of the browser's broken-image glyph. */
|
|
436
|
+
.screen-unavailable-msg { display: none; box-sizing: border-box; width: 300px;
|
|
437
|
+
max-width: 100%; min-height: 480px; max-height: calc(100vh - 100px);
|
|
438
|
+
flex-direction: column; align-items: center; justify-content: center; gap: 8px;
|
|
439
|
+
padding: 24px; text-align: center; color: var(--text-dim); }
|
|
440
|
+
.screen-unavailable-title { font-size: 14px; font-weight: 600; color: var(--text); }
|
|
441
|
+
.screen-unavailable-sub { font-size: 12.5px; }
|
|
442
|
+
#screen-host.screen-unavailable #screen-img { display: none; }
|
|
443
|
+
#screen-host.screen-unavailable .screen-unavailable-msg { display: flex; }
|
|
434
444
|
#highlight { position: absolute; border: 2px solid var(--accent);
|
|
435
445
|
background: rgba(9,105,218,0.12); box-shadow: 0 0 0 9999px rgba(0,0,0,0.40) inset;
|
|
436
446
|
pointer-events: none; transition: all 0.12s ease-out; }
|
|
@@ -1289,6 +1299,10 @@ export const INSPECTOR_HTML = `<!doctype html>
|
|
|
1289
1299
|
</div>
|
|
1290
1300
|
<div id="screen-host">
|
|
1291
1301
|
<img id="screen-img" alt="device screen" />
|
|
1302
|
+
<div class="screen-unavailable-msg">
|
|
1303
|
+
<div class="screen-unavailable-title">Device screen unavailable</div>
|
|
1304
|
+
<div class="screen-unavailable-sub">Couldn't capture the device — retrying…</div>
|
|
1305
|
+
</div>
|
|
1292
1306
|
<div id="highlight" style="display:none"></div>
|
|
1293
1307
|
<div id="screen-action-overlay" class="screen-action-overlay" aria-hidden="true">
|
|
1294
1308
|
<div class="screen-action-card">
|
|
@@ -2036,6 +2050,12 @@ await mobile.getByUiSelector('new UiSelector().description("Login")').click();</
|
|
|
2036
2050
|
if (autoRefreshOn) scheduleNextRefresh(Math.max(base, elapsed));
|
|
2037
2051
|
}
|
|
2038
2052
|
|
|
2053
|
+
// Toggle the "device screen unavailable" fallback (shown when a snapshot
|
|
2054
|
+
// fails or returns no screenshot, so we never render a broken <img>).
|
|
2055
|
+
function setScreenUnavailable(on) {
|
|
2056
|
+
$('screen-host').classList.toggle('screen-unavailable', !!on);
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2039
2059
|
async function fetchSnapshot(opts) {
|
|
2040
2060
|
const force = opts && opts.force;
|
|
2041
2061
|
if (snapshotInFlight) {
|
|
@@ -2065,7 +2085,14 @@ await mobile.getByUiSelector('new UiSelector().description("Login")').click();</
|
|
|
2065
2085
|
state.sourceXml = j.source;
|
|
2066
2086
|
$('session-meta').textContent = formatSessionMeta(j.platform, j.project);
|
|
2067
2087
|
$('screen-meta').textContent = j.viewport.w + ' × ' + j.viewport.h;
|
|
2068
|
-
|
|
2088
|
+
// Only set the image when there's an actual screenshot — an empty/missing
|
|
2089
|
+
// one would render as a broken <img>; show the fallback instead.
|
|
2090
|
+
if (typeof j.screenshot === 'string' && j.screenshot.length > 0) {
|
|
2091
|
+
$('screen-img').src = 'data:image/png;base64,' + j.screenshot;
|
|
2092
|
+
setScreenUnavailable(false);
|
|
2093
|
+
} else {
|
|
2094
|
+
setScreenUnavailable(true);
|
|
2095
|
+
}
|
|
2069
2096
|
renderTree();
|
|
2070
2097
|
if (hierarchyMode === 'xml') refreshHierarchyXml();
|
|
2071
2098
|
if (prevXpath && prevSig) {
|
|
@@ -2089,6 +2116,7 @@ await mobile.getByUiSelector('new UiSelector().description("Login")').click();</
|
|
|
2089
2116
|
setStatus('idle');
|
|
2090
2117
|
} catch (err) {
|
|
2091
2118
|
setStatus('error: ' + err.message);
|
|
2119
|
+
setScreenUnavailable(true);
|
|
2092
2120
|
} finally {
|
|
2093
2121
|
snapshotInFlight = false;
|
|
2094
2122
|
}
|
|
@@ -2716,6 +2744,10 @@ await mobile.getByUiSelector('new UiSelector().description("Login")').click();</
|
|
|
2716
2744
|
};
|
|
2717
2745
|
}
|
|
2718
2746
|
|
|
2747
|
+
// A corrupt/truncated data URI fails to decode — fall back rather than show
|
|
2748
|
+
// the browser's broken-image glyph.
|
|
2749
|
+
$('screen-img').addEventListener('error', () => setScreenUnavailable(true));
|
|
2750
|
+
|
|
2719
2751
|
$('screen-img').addEventListener('mouseup', (ev) => {
|
|
2720
2752
|
const pt = imgToDevice(ev);
|
|
2721
2753
|
// Pick mode (Record tab) takes priority — consume one click then dismiss.
|
|
@@ -4250,8 +4282,22 @@ await mobile.getByUiSelector('new UiSelector().description("Login")').click();</
|
|
|
4250
4282
|
updateConnectSummary();
|
|
4251
4283
|
}
|
|
4252
4284
|
|
|
4285
|
+
// Whether the wizard is allowed to advance forward off the given step (its
|
|
4286
|
+
// prerequisites are met). Mirrors the gating in updateConnectSummary.
|
|
4287
|
+
function canAdvanceFrom(step) {
|
|
4288
|
+
if (step === 1) {
|
|
4289
|
+
return isCloudMode() ? cloudCredsValid : $('appium-pill').classList.contains('live');
|
|
4290
|
+
}
|
|
4291
|
+
if (step === 2) return !!$('cap-device').value.trim();
|
|
4292
|
+
return true;
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4253
4295
|
function goToStep(n) {
|
|
4254
4296
|
if (n < 1 || n > 3) return;
|
|
4297
|
+
// Hard-gate forward navigation: never advance past a step whose
|
|
4298
|
+
// prerequisites aren't met — even for programmatic callers like the guided
|
|
4299
|
+
// tour. Backward navigation and re-selecting the current step are free.
|
|
4300
|
+
if (n > wizardStep && !canAdvanceFrom(wizardStep)) return;
|
|
4255
4301
|
wizardStep = n;
|
|
4256
4302
|
document.querySelectorAll('.wizard-page').forEach((p) => {
|
|
4257
4303
|
p.classList.toggle('active', Number(p.getAttribute('data-page')) === n);
|
|
@@ -5188,6 +5234,34 @@ await mobile.getByUiSelector('new UiSelector().description("Login")').click();</
|
|
|
5188
5234
|
const t = document.querySelector('.tab[data-tab="' + name + '"]');
|
|
5189
5235
|
if (t) t.click();
|
|
5190
5236
|
}
|
|
5237
|
+
// Live tour only: the Locators / Attributes panels are empty until an element
|
|
5238
|
+
// is selected, so the tour would spotlight a blank panel. If the user hasn't
|
|
5239
|
+
// selected anything yet, auto-select a representative node (one with an id /
|
|
5240
|
+
// text / content-desc, else the first node) so those steps show real content.
|
|
5241
|
+
function tourEnsureSelection() {
|
|
5242
|
+
if (state.selected) return;
|
|
5243
|
+
let pick = null;
|
|
5244
|
+
for (const [, el] of state.nodeMap) {
|
|
5245
|
+
if (!el || !el.getAttribute) continue;
|
|
5246
|
+
if (
|
|
5247
|
+
el.getAttribute('resource-id') ||
|
|
5248
|
+
el.getAttribute('text') ||
|
|
5249
|
+
el.getAttribute('content-desc') ||
|
|
5250
|
+
el.getAttribute('name') ||
|
|
5251
|
+
el.getAttribute('label')
|
|
5252
|
+
) {
|
|
5253
|
+
pick = el;
|
|
5254
|
+
break;
|
|
5255
|
+
}
|
|
5256
|
+
}
|
|
5257
|
+
if (!pick) {
|
|
5258
|
+
for (const [, el] of state.nodeMap) {
|
|
5259
|
+
pick = el;
|
|
5260
|
+
break;
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
if (pick) selectElement(pick);
|
|
5264
|
+
}
|
|
5191
5265
|
// Switch the demo stage's mock right-hand tab (Record / Script / Locators / Attributes).
|
|
5192
5266
|
function showDemoTab(name) {
|
|
5193
5267
|
['rec', 'script', 'loc', 'attrs'].forEach((k) => {
|
|
@@ -5230,9 +5304,9 @@ await mobile.getByUiSelector('new UiSelector().description("Login")').click();</
|
|
|
5230
5304
|
body: 'Press <b>Start record</b>, select an element, then choose an action — Click, Type, Clear, gestures… The <b>Actions / Screen / Assertions</b> sub-tabs switch what you capture. Each step is appended live.' },
|
|
5231
5305
|
{ sel: '#tab-script', before: function () { tourClickTab('script'); }, title: 'Recorded script',
|
|
5232
5306
|
body: 'Your test in <b>Taqwright</b> (runnable), or <b>Python</b> / <b>Java</b> (steps only). Use <b>⎘ Copy</b>, <b>↓ Export</b> (saves into your tests folder), or Clear.' },
|
|
5233
|
-
{ sel: '#tab-locators', before: function () { tourClickTab('locators'); }, title: 'Locators',
|
|
5307
|
+
{ sel: '#tab-locators', before: function () { tourEnsureSelection(); tourClickTab('locators'); }, title: 'Locators',
|
|
5234
5308
|
body: 'Ranked, uniqueness-verified selectors for the selected element — id, accessibility id, UIAutomator / NSPredicate / Class Chain, xpath. The <b>recommended</b> pick is on top; click any to copy.' },
|
|
5235
|
-
{ sel: '#tab-attrs', before: function () { tourClickTab('attrs'); }, title: 'Attributes',
|
|
5309
|
+
{ sel: '#tab-attrs', before: function () { tourEnsureSelection(); tourClickTab('attrs'); }, title: 'Attributes',
|
|
5236
5310
|
body: 'The selected element\\'s full attribute set (resource-id, class, text, content-desc, bounds…) plus its xpath.' },
|
|
5237
5311
|
{ sel: '#btn-disconnect', before: function () { tourClickTab('record'); }, title: 'Done',
|
|
5238
5312
|
body: 'When finished, <b>Disconnect</b> ends the session and returns to setup. Reopen this tour any time with <b>? Help</b>.' },
|
package/package.json
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taqwright/taqwright",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "E2E mobile testing on the Playwright runner",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
|
-
"repository": {
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/taqelah/taqwright.git"
|
|
9
|
-
},
|
|
10
6
|
"type": "module",
|
|
11
7
|
"main": "dist/index.js",
|
|
12
8
|
"types": "dist/index.d.ts",
|
|
@@ -44,6 +40,7 @@
|
|
|
44
40
|
"format:check": "prettier --check .",
|
|
45
41
|
"test": "npm run build && node --test test/*.test.js",
|
|
46
42
|
"test:watch": "node --test --watch test/*.test.js",
|
|
43
|
+
"test:e2e": "playwright test --config e2e/playwright.config.ts",
|
|
47
44
|
"test:coverage": "npm run build && node --test --experimental-test-coverage test/*.test.js",
|
|
48
45
|
"prepare": "npm run build",
|
|
49
46
|
"prepublishOnly": "npm run build",
|