判据: 管理页设备判据(到期静态判据的第一条升级)+ 修设备判据基建的三个真 bug
`run-all.mjs` 的 `STATIC_ONLY` 里登记着 6 条"只能静态验"的判据,前提是
「本工作区能装、能点设备」。设备现在可用 ⇒ 它们**到期**了(欠账当场变红)。
这一批升级第一条:`harmony-admin`(用户管理页)。
## 一、新设备判据:管理页真的能打开、列表真的渲染
上面 27 条静态判据判的都是逻辑与接线形态。它们全绿时,
"管理页能不能打开、列表能不能渲染"**一句都没验过** —— 而它最容易坏:
路由注册对了但入口没接上、接口回来了但列表没渲染。
判据自己搭现场:拉起应用 → 进「我的」→ 滚到底 → 点「管理」→
断言真的在管理页(出现「新建用户」)且**渲染出用户行**。
## 二、途中撞出三个**判据自身**的 bug(都已修,都写了教训)
1. **`tapText` 从来没成功点过任何东西**(`lib/harmony-device.mjs`)
`findByText` 返回的是**数组**,我当单个节点用了 ⇒ `node.attributes` 恒
`undefined` ⇒ 恒返回 `false`。症状极隐蔽:调用方以为"没找到那个文案",
实际是帮手自己坏了。修:从数组里挑,且**优先挑可点的那个**
(同名文案常常一个可点、一个不可点)。
2. **判据之间互相干扰**(新增 `backToMain`)
每个设备判据都是写操作,会把前台留在它操作完的那一页。`harmony-admin`
把人留在管理页 —— 那是 `pushUrl` 出去的独立 `@Entry` 页,**没有侧栏**
⇒ 后面的 `cross-client-gesture` / `harmony-appearance` 找不到侧栏、
双双 skip(skip 原因写的是"宽屏侧栏找不到",看起来像功能没了)。
`launchOurApp` 解决不了(`aa start` 只切前台,不弹栈)⇒ 新增 `backToMain`。
3. **"找不到元素"要先分清是功能缺失还是判据没摆好现场**
本判据连栽四种形态,每一种都伪装成"功能缺失":
· 锚点文案错(入口是「管理」,我按「用户管理」找 —— 后者只是说明的一部分)
· 元素在滚动下方(dumpLayout 只报可见节点)
· 文字节点不可点(`.onClick` 在包住它的容器上,ArkUI 的常态)
· **滚动步长跨过了它**(诊断打印现场才发现:停在了「系统通知」那一带,
而「管理」只占 ~0.04 屏,一次 0.2 屏的滑动必然越过)
⇒ 最后改成"**先滚到底、再小步回扫**"(只管往前找在不均匀列表上必漏),
并加了 `AGENTMAIL_ADMIN_DEBUG=1` 的诊断入口把现场打出来。
## 三、判据
`run-all.mjs` → `files=32 ran=32 checks=505 pass=505 fail=0 skip=0
red=0 broken=0 unreported=0`(连跑两次稳定)。
`harmony-admin` 27 → 28 条。`hvigorw assembleHap` 成功;前端重建。
**设备实测**:管理页从「我的」页打开,显示「管理 / 用户管理 3 / 新建用户」
与三行用户(jianf 管理员 / gui-lab 用户 / test 用户)。
**仍到期未升级**:`harmony-appearance`(已加 2 条设备判据,但登记里其余部分仍静态)、
`harmony-logic`、`cross-client-theme`、`appearance-defaults`、`harmony-imageprep`
—— 按"每条缺什么设备侧验证"逐条来,不为了消数字而凑。
This commit is contained in:
@ -41,7 +41,7 @@ import { code, prose } from './lib/read.mjs';
|
||||
*/
|
||||
import {
|
||||
findHdc, hasTarget, dumpLayout, walk, swipe, tapText,
|
||||
launchOurApp, boundsCenter, tap,
|
||||
launchOurApp, boundsCenter, tap, backToMain,
|
||||
} from './lib/harmony-device.mjs';
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
@ -278,7 +278,14 @@ test('★ 设备:在网格列上左滑,日历标题真的翻到下一段', a
|
||||
* 这才是设备判据该有的样子(前置条件自己满足,而不是等人摆好)。
|
||||
*/
|
||||
assert.ok(await launchOurApp(hdc), '要能拉起我们的应用并等到它到前台');
|
||||
await new Promise((r) => setTimeout(r, 1500));
|
||||
/*
|
||||
* ★★ 必须先回**主界面**:前面跑过的设备判据可能把前台留在
|
||||
* push 出去的独立页(管理页/详情页/写信页)—— 那些页面**没有侧栏**,
|
||||
* 于是这里找不到侧栏项、判据会 skip 成"导航轨只找到 1 项"。
|
||||
* 那是**判据间干扰**,不是功能缺失(详见 `backToMain` 的注释)。
|
||||
*/
|
||||
assert.ok(await backToMain(hdc), '要能回到主界面(侧栏/底栏可见)');
|
||||
await new Promise((r) => setTimeout(r, 1200));
|
||||
|
||||
/* 切到日历窗格:宽屏点侧栏第 2 项 / 窄屏点底栏「日历」 */
|
||||
const rootA = dumpLayout(hdc);
|
||||
|
||||
@ -526,3 +526,202 @@ test('★ 「我的」页的数据都来自服务端真实字段(不是写死/
|
||||
assert.ok(key.includes('key_token'),
|
||||
'UserKey 要有 key_token —— 密钥全文只在创建响应里出现一次,没有它就没法展示');
|
||||
});
|
||||
|
||||
/* ═══════════════ 设备侧:管理页真的能打开、能操作 ═══════════════ */
|
||||
|
||||
test('★ 设备:管理页能从「我的」页打开,且列表真的渲染出用户', async (t) => {
|
||||
/*
|
||||
* 这条补的是 `run-all.mjs` 的 `STATIC_ONLY` 登记里说的那个缺口:
|
||||
* 「用户管理页:`.ets` 页面要 hvigorw 才能编译、要设备才能点(本机两者都没有)」
|
||||
*
|
||||
* 上面 27 条判的都是**逻辑与接线形态**。它们全绿时,"管理页能不能打开、
|
||||
* 列表能不能渲染"这件事**一句都没验过** —— 而它偏偏最容易坏:
|
||||
* 路由注册对了但入口没接上、接口回来了但列表没渲染、非管理员也能看见入口。
|
||||
*
|
||||
* ★★ 写这条判据时连撞三个坑,每个都是**判据自己的**错(记下来,它们是同类):
|
||||
* ① 锚点文案写错:入口标题是「管理」,我却按「用户管理」找
|
||||
* (后者只是它下面那行说明的一部分)⇒ **永远跳过**。
|
||||
* 而那个跳过看起来像"非管理员、行为正确"—— 最危险的那种静默。
|
||||
* ② 元素在**滚动下方**:dumpLayout 只报可见节点 ⇒ 不滚到底根本搜不到。
|
||||
* ③ 文字节点不可点:`Text('管理')` 的 `clickable=false`,
|
||||
* 点击挂在包住它的容器上(ArkUI 的常态)。
|
||||
*
|
||||
* ⇒ 结论:设备判据里"找不到元素"必须先分清是**功能缺失**还是**判据没摆好现场**。
|
||||
* 前两个坑都是后者,而它们伪装成前者。
|
||||
*/
|
||||
const D = await import('./lib/harmony-device.mjs');
|
||||
const hdc = D.findHdc();
|
||||
if (!hdc || !D.hasTarget(hdc)) {
|
||||
return t.skip('设备不在 —— 本条的设备半边本次不跑(上面静态层仍把住逻辑与接线)');
|
||||
}
|
||||
assert.ok(await D.launchOurApp(hdc), '要能拉起应用并等到它到前台');
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
|
||||
const dims = () => {
|
||||
let w = 0;
|
||||
let h = 0;
|
||||
for (const n of D.walk(D.dumpLayout(hdc))) {
|
||||
const m = /\[\d+,\d+\]\[(\d+),(\d+)\]/.exec(n.attributes?.bounds || '');
|
||||
if (m) {
|
||||
w = Math.max(w, Number(m[1]));
|
||||
h = Math.max(h, Number(m[2]));
|
||||
}
|
||||
}
|
||||
return { w, h };
|
||||
};
|
||||
const hasText = (needle) => [...D.walk(D.dumpLayout(hdc))]
|
||||
.some((n) => (n.attributes?.text || '').includes(needle));
|
||||
|
||||
/* 已经在管理页了?那直接用(判据不该强求"必须从某处点进来") */
|
||||
if (!hasText('新建用户')) {
|
||||
const { w: scrW, h: scrH } = dims();
|
||||
const wide = scrW / scrH > 1.2;
|
||||
|
||||
/*
|
||||
* ① 进「我的」
|
||||
*
|
||||
* ★★ 「在「我的」页」**不能用单个文案判**(第一版用「用户名」⇒ 假红):
|
||||
* 本页有 8 个分段、可滚动,而**判据里的滚动会把「用户名」滚出屏幕**
|
||||
* —— 同一个判据第二次跑到这里时(或前面判据把它留在这页且已滚动),
|
||||
* `hasText('用户名')` 为假,于是它以为"不在「我的」页"、又去点一次导航,
|
||||
* 结果页面被滚到别处、后面全乱。
|
||||
*
|
||||
* 改用**分段标记**判定:这些文案分布在本页的不同段落,
|
||||
* 滚动到任何位置都至少能看见一个。
|
||||
*/
|
||||
const inMePane = () => ['用户名', '多账号', '外观', '客户端连接密钥', '修改密码']
|
||||
.some((marker) => hasText(marker));
|
||||
if (!inMePane()) {
|
||||
if (wide) {
|
||||
const avatar = [...D.walk(D.dumpLayout(hdc))].find((n) => {
|
||||
const a = n.attributes || {};
|
||||
if (a.clickable !== 'true') return false;
|
||||
const m = /\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]/.exec(a.bounds || '');
|
||||
if (!m) return false;
|
||||
const [, , y1, x2, y2] = m.map(Number);
|
||||
return x2 <= scrW * 0.08 && y1 > scrH * 0.5 && (y2 - y1) > 80;
|
||||
});
|
||||
if (!avatar) return t.skip('宽屏侧栏头像找不到 —— 无法进「我的」');
|
||||
const c = D.boundsCenter(avatar.attributes.bounds);
|
||||
assert.ok(D.tap(hdc, c.cx, c.cy), '要能点到侧栏头像');
|
||||
} else {
|
||||
assert.ok(D.tapText(hdc, '我的'), '要能点到「我的」');
|
||||
}
|
||||
let landed = false;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
if (inMePane()) { landed = true; break; }
|
||||
}
|
||||
assert.ok(landed, '要能进「我的」页');
|
||||
}
|
||||
|
||||
/*
|
||||
* ② 找「管理」入口(在 8 个分段之后,屏幕外)。
|
||||
*
|
||||
* ★★ 滚动必须**小步 + 每步检查**(我前两版都栽在这里):
|
||||
* 大步滑(`0.8h → 0.25h`)会**跳过**那个入口 —— 实测:滚 1~2 次时它在屏外,
|
||||
* 第 3 次才出现(`y=1994`,屏高 2232,贴着底)。而"滑到底再看"
|
||||
* 会因为惯性/回弹停在别处,于是"找不到"——看起来像功能缺失。
|
||||
*
|
||||
* 现在每步只滑 ~1/4 屏,滚动后立刻查一次。
|
||||
*/
|
||||
/*
|
||||
* ★★ 第四版(诊断逼出来的真相):小步滚也会**越过**它。
|
||||
* 实测现场:滚到「系统通知」那一带(`03:50` 附近)时,「管理」**已经在上面**了
|
||||
* —— 它在「客户端连接密钥」之后、「系统通知」之前,而我的步长一次跨过了整个
|
||||
* 区间(每次 ~0.2 屏,而它只占 ~0.04 屏)。
|
||||
*
|
||||
* ⇒ 正确策略是**先滚到底,再从下往上找**(到底之后它一定在屏幕内或其上方,
|
||||
* 再按小步回滚就必然扫过它)。"只管往前找"在不均匀列表上必然漏。
|
||||
*/
|
||||
const scrollDown = async (steps) => {
|
||||
for (let i = 0; i < steps; i++) {
|
||||
D.swipe(hdc, Math.round(scrW * 0.5), Math.round(scrH * 0.7),
|
||||
Math.round(scrW * 0.5), Math.round(scrH * 0.3), 2500);
|
||||
await new Promise((r) => setTimeout(r, 600));
|
||||
}
|
||||
};
|
||||
/* ① 一口气滚到底(多滚几次确保到底) */
|
||||
await scrollDown(8);
|
||||
/* ② 若「管理」已经在屏上就好了;否则小步**回滚**(内容往下走)扫回来 */
|
||||
let found = hasText('管理');
|
||||
for (let i = 0; i < 8 && !found; i++) {
|
||||
D.swipe(hdc, Math.round(scrW * 0.5), Math.round(scrH * 0.35),
|
||||
Math.round(scrW * 0.5), Math.round(scrH * 0.6), 1500);
|
||||
await new Promise((r) => setTimeout(r, 650));
|
||||
found = hasText('管理');
|
||||
}
|
||||
if (!found) {
|
||||
const seen = [...D.walk(D.dumpLayout(hdc))]
|
||||
.map((n) => (n.attributes?.text || '').trim())
|
||||
.filter(Boolean).slice(-20).join(' | ');
|
||||
return t.skip(`滚到底后仍找不到「管理」入口(非管理员时这是正确行为)—— 末尾读到:${seen}`);
|
||||
}
|
||||
|
||||
/*
|
||||
* ③ 点它。文字节点不可点(`.onClick` 在包住它的容器上),所以:
|
||||
* 先让 `tapText` 试(它会挑同名的**可点**节点),失败再退回点文字中心
|
||||
* (命中测试会冒泡到祖先的 onClick)。
|
||||
*
|
||||
* ★ 兜底那一步要**重新 dump 并当场找**,不能复用上面 `found` 那次的快照 ——
|
||||
* 实测(套件里红、单独跑绿):`found` 为真说明"那一瞬间有",
|
||||
* 而下一步再 dump 时可能正在重绘/滚动的惯性里 ⇒ 快照里没有 ⇒ 假红。
|
||||
* 设备上的两次 dump 之间窗口状态是可以变的,别假设它们一致。
|
||||
*/
|
||||
/*
|
||||
* ★★ 第三版:**直接按文字节点的矩形中心点**,不走 `tapText`。
|
||||
*
|
||||
* 为什么 `tapText` 在这里不可靠(实测:单独跑绿、套件里红):
|
||||
* 它先找"同名且**可点**"的节点;这个入口的 `.onClick` 挂在
|
||||
* **外面那层 `Column`** 上(`SettingsPage.ets:1144`),而那层的
|
||||
* bounds 比文字大一圈、且**没有文字** ⇒ `findByText` 找不到它,
|
||||
* `tapText` 退回点文字中心;但文字节点的中心在**卡片内部**,
|
||||
* 命中测试是否冒泡到那层 Column 取决于层级细节 —— 不稳定。
|
||||
*
|
||||
* 而实测(手动点 `(1592, 2017)`,即文字所在行的中心)**每次都进管理页**。
|
||||
* ⇒ 用文字节点的**矩形中心**直接点,这条路径最接近"人点那一行"。
|
||||
*/
|
||||
/* 诊断入口:`AGENTMAIL_ADMIN_DEBUG=1` 时把现场打出来(排查设备判据专用) */
|
||||
const dbg = process.env.AGENTMAIL_ADMIN_DEBUG
|
||||
? (m) => console.log(`[admin] ${m}`) : () => {};
|
||||
const snap = D.dumpLayout(hdc);
|
||||
const node = [...D.walk(snap)]
|
||||
.find((n) => (n.attributes?.text || '').trim() === '管理');
|
||||
if (!node) {
|
||||
const seen = [...D.walk(snap)]
|
||||
.map((n) => (n.attributes?.text || '').trim()).filter(Boolean).slice(-18).join(' | ');
|
||||
dbg(`找不到「管理」节点;末尾文本:${seen}`);
|
||||
}
|
||||
assert.ok(node, '滚到之后「管理」文字节点应在 dump 里');
|
||||
const c = D.boundsCenter(node.attributes.bounds);
|
||||
assert.ok(c, '要能解析出「管理」的坐标');
|
||||
/*
|
||||
* 点之前**再确认一次前台是我们的应用**:套件里前面跑过的判据可能把前台
|
||||
* 切走(那是"判据间干扰"的另一种形态)。切走了就不是"点不到入口",
|
||||
* 而是"点在了别人的界面上"—— 后者必须显式失败,不能算通过。
|
||||
*/
|
||||
assert.equal(D.foregroundBundle(hdc), D.ourBundle(),
|
||||
'点之前前台必须还是我们的应用(前面判据若切走了前台,这里点的是别人的界面)');
|
||||
const ok = D.tap(hdc, c.cx, c.cy);
|
||||
dbg(`点 (${c.cx},${c.cy}) ⇒ ${ok}`);
|
||||
assert.ok(ok, `要能点到「管理」入口(坐标 ${c.cx},${c.cy})`);
|
||||
}
|
||||
|
||||
/* ④ 等页面真的切过去 */
|
||||
let onAdmin = false;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
if (hasText('新建用户')) { onAdmin = true; break; }
|
||||
}
|
||||
assert.ok(onAdmin,
|
||||
'★ 点「管理」之后要真的在**管理页**(标志:出现「新建用户」按钮)—— ' +
|
||||
'只断言"点到了入口"证明不了页面切过去了(路由没注册时点击静默无效)');
|
||||
|
||||
/* ⑤ 列表要真的渲染出用户行(抓"接口回来了但列表没接上") */
|
||||
const root = D.dumpLayout(hdc);
|
||||
const texts = [...D.walk(root)].map((n) => (n.attributes?.text || '').trim()).filter(Boolean);
|
||||
const userRows = texts.filter((x) => /用户管理|启用|管理员|用户$/.test(x));
|
||||
assert.ok(userRows.length > 0,
|
||||
'★ 管理页要真的渲染出**用户行**(带角色/状态的那种)—— ' +
|
||||
`只有标题而没有行,说明数据没渲染。实际读到:${texts.slice(0, 25).join(' | ')}`);
|
||||
});
|
||||
|
||||
@ -1226,6 +1226,8 @@ test('★ 设备:窗格内容不得超出屏幕(「我的」页滑杆数值
|
||||
return t.skip('设备不在 —— 本条的设备半边本次不跑');
|
||||
}
|
||||
assert.ok(await D.launchOurApp(hdc), '要能拉起应用');
|
||||
/* 回主界面(前面判据可能把前台留在 push 出去的页上 —— 那些页没有侧栏) */
|
||||
assert.ok(await D.backToMain(hdc), '要能回到主界面');
|
||||
|
||||
/*
|
||||
* 逐个窗格检查。宽屏下「我的」用侧栏头像进,窄屏用底栏 —— 这里只跑宽屏那份
|
||||
|
||||
@ -367,9 +367,92 @@ export async function launchOurApp(hdc, { settle = 400, tries = 40 } = {}) {
|
||||
export function tapText(hdc, text) {
|
||||
if (!hdc) return false;
|
||||
const root = dumpLayout(hdc);
|
||||
const node = findByText(root, text);
|
||||
if (node === null) return false;
|
||||
const c = boundsCenter(node.attributes?.bounds);
|
||||
/*
|
||||
* ★★ 2026-09-19 修(真 bug,而且是我自己写的帮手):
|
||||
* `findByText` 返回的是**数组**(同名节点可能多个),而我当单个节点用了
|
||||
* ⇒ `node.attributes` 恒为 `undefined` ⇒ 这个帮手**从来没成功点过任何东西**。
|
||||
*
|
||||
* 它造成的症状极其隐蔽:`tapText(...)` 返回 `false`,调用方以为
|
||||
* "没找到那个文案"——而实际是**帮手自己坏了**。用它的判据(appearance/admin)
|
||||
* 都会表现为"找不到入口"或"点不到",看起来像功能缺失。
|
||||
*
|
||||
* 修两处:① 从数组里挑;② **优先挑可点的那个** —— 同名文案常常一个可点
|
||||
* (入口按钮)、一个不可点(说明文字),随便挑会点到说明文字上(点了没反应)。
|
||||
*/
|
||||
const nodes = findByText(root, text);
|
||||
if (nodes.length === 0) return false;
|
||||
const target = nodes.find((n) => n.attributes?.clickable === 'true') || nodes[0];
|
||||
/*
|
||||
* ★★ 2026-09-19 补第三层:文字节点本身常常**不可点**,点击挂在它**祖先**上。
|
||||
*
|
||||
* 实测(「我的」页的管理入口):`Text('管理')` 的 `clickable=false`,
|
||||
* 而 `.onClick` 写在包住它的那层 `Row` 上 —— 这是 ArkUI 的常态
|
||||
* (文字只是内容,可点的是容器)。直接点文字节点的中心**也能触到**
|
||||
* (命中测试会冒泡到祖先),但那样判据就依赖"冒泡行为"而不是"我点到了那个入口"。
|
||||
*
|
||||
* 所以这里显式用**祖先的可点容器**的边界:找不到可点祖先时才退回文字节点自己。
|
||||
* 当前实现从目标节点的 bounds 出发做一次"就近扩展":
|
||||
* 若目标不可点,就用**同文本的可点兄弟/祖先链**(dump 树里 clickable 的那一层
|
||||
* 一般把文字整个包住,所以用它的 bounds 最稳)。
|
||||
*/
|
||||
const clickableAncestor = (() => {
|
||||
if (target.attributes?.clickable === 'true') return target;
|
||||
/* 往上找:dump 树的节点带 children,父链要从根重新走一遍才能拿到 */
|
||||
let found = null;
|
||||
const visit = (n) => {
|
||||
if (found) return;
|
||||
const kids = n.children || [];
|
||||
for (const k of kids) visit(k);
|
||||
if (found) return;
|
||||
const self = n.attributes || {};
|
||||
if (self.clickable === 'true'
|
||||
&& String(self.bounds || '') === String(target.attributes?.bounds || '')
|
||||
&& kids.length > 0) {
|
||||
found = n;
|
||||
}
|
||||
};
|
||||
visit(root);
|
||||
return found || target;
|
||||
})();
|
||||
const c = boundsCenter(clickableAncestor.attributes?.bounds);
|
||||
if (c === null) return false;
|
||||
return tap(hdc, c.cx, c.cy);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 确保回到**主界面**(`MainPage`),而不是某个 push 出去的页
|
||||
* (管理页 / 详情页 / 写信页 —— 它们没有侧栏与底栏)。
|
||||
*
|
||||
* ★★ 为什么需要它(2026-09-19 实测撞出来的**判据间干扰**):
|
||||
* 每个设备判据都是"写操作",会把前台留在它操作完的那一页上。
|
||||
* 于是**前一个判据留下的位置**决定了后一个判据能不能跑:
|
||||
* `harmony-admin` 把人留在管理页(那是 `pushUrl` 出去的独立 @Entry 页,
|
||||
* **没有侧栏**)⇒ 后面的 `cross-client-gesture` / `harmony-appearance`
|
||||
* 找不到宽屏侧栏 ⇒ 双双 skip。而它们的 skip 原因写的是
|
||||
* "宽屏侧栏找不到",看起来像功能没了。
|
||||
*
|
||||
* `launchOurApp` 解决不了这个:`aa start` 只把应用切到前台,
|
||||
* 不会把已经 push 的页面弹栈。
|
||||
*
|
||||
* 做法:**反复按返回键**直到侧栏/底栏出现(那是主界面的标志)。
|
||||
* 上限兜底,避免在没有可弹页面时空按。
|
||||
*/
|
||||
export async function backToMain(hdc, { tries = 6, settle = 700 } = {}) {
|
||||
if (!hdc) return false;
|
||||
const atMain = () => {
|
||||
const root = dumpLayout(hdc);
|
||||
for (const n of walk(root)) {
|
||||
const t = (n.attributes?.text || '').trim();
|
||||
/* 侧栏导航轨(宽屏)或底栏(窄屏)上的项 —— 只有 MainPage 有 */
|
||||
if (t === '通信' || t === '联系') return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for (let i = 0; i < tries; i++) {
|
||||
if (atMain()) return true;
|
||||
sh(hdc, ['shell', 'uitest', 'uiInput', 'keyEvent', 'Back'], 10000);
|
||||
await new Promise((r) => setTimeout(r, settle));
|
||||
}
|
||||
return atMain();
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ const SUITE = [
|
||||
['test/criteria-hygiene.test.mjs', [], 6],
|
||||
// 用户管理页(P4c 同批):动作↔服务端调用同名 / 门禁只认严格 admin /
|
||||
// 启停只发 status / 「受限」徽标口径 / 页面零写死色值 / 接线(纯逻辑真被调用)
|
||||
['test/harmony-admin.test.mjs', ['--experimental-strip-types', '--no-warnings'], 27],
|
||||
['test/harmony-admin.test.mjs', ['--experimental-strip-types', '--no-warnings'], 28],
|
||||
// P4c 图片上传:阈值与两档策略 / 失败必带原因 / 退档判定只有一处 /
|
||||
// release 都 await / 解码按目标尺寸 / multipart 字段名 / 上传后重新同步
|
||||
['test/harmony-imageprep.test.mjs', ['--experimental-strip-types', '--no-warnings'], 30],
|
||||
|
||||
Reference in New Issue
Block a user