feat(harmony): add folder transfers and HDC device management
This commit is contained in:
@@ -63,11 +63,42 @@ const entries = [
|
||||
];
|
||||
window.__TROVE_CONTEXT__ = {
|
||||
curioId: "dev.trove.harmony-connector",
|
||||
version: "0.1.1",
|
||||
version: "0.2.0",
|
||||
instanceId: "ui-fixture",
|
||||
platform: "macos",
|
||||
};
|
||||
window.__TROVE_BRIDGE__ = createMockBridge({
|
||||
"@self.hdc.status": () => ({ busy: false, job: null }),
|
||||
"@self.hdc.tool": () => ({
|
||||
available: true,
|
||||
version: "HDC · 模拟数据",
|
||||
path: "/fixture/official-sdk/hdc",
|
||||
}),
|
||||
"@self.hdc.devices": () => ({
|
||||
devices: attached
|
||||
? [{ id: "fixture-device", transport: "USB", state: "Connected" }]
|
||||
: [],
|
||||
}),
|
||||
"@self.hdc.inspect": () => ({
|
||||
capabilities: Object.fromEntries(
|
||||
["apps", "launch", "screenshot", "logs", "report"].map((key) => [
|
||||
key,
|
||||
{ available: true },
|
||||
]),
|
||||
),
|
||||
info: { text: "HUAWEI Pura 70 Ultra · 界面测试数据" },
|
||||
}),
|
||||
"@self.hdc.apps": () => ({
|
||||
apps: ["com.example.gallery", "com.example.notes", "com.example.reader"],
|
||||
}),
|
||||
"@self.hdc.app": ({ bundle }) => ({
|
||||
bundle,
|
||||
versionName: "1.2.0",
|
||||
versionCode: 12,
|
||||
debug: bundle.endsWith("notes"),
|
||||
abilities: [{ name: "Home", module: "entry" }],
|
||||
details: {},
|
||||
}),
|
||||
"@self.info": () => ({ connection, busy: false }),
|
||||
"@self.devices": () => ({ devices: attached ? [device] : [] }),
|
||||
"@self.monitor": () => ({
|
||||
@@ -104,6 +135,15 @@ window.__TROVE_BRIDGE__ = createMockBridge({
|
||||
"@self.transferStatus": () => {
|
||||
if (job?.state === "running") {
|
||||
job.sent += 524288;
|
||||
job.statistics = {
|
||||
bytesPerSecond: 1310720,
|
||||
averageBytesPerSecond: 1210720,
|
||||
elapsedSeconds: job.sent / 1310720,
|
||||
remainingSeconds: (job.total - job.sent) / 1310720,
|
||||
filesTotal: 3,
|
||||
filesCompleted: Math.floor((job.sent / job.total) * 3),
|
||||
currentFile: "Documents/设计稿.zip",
|
||||
};
|
||||
if (job.sent >= job.total) {
|
||||
job.state = "completed";
|
||||
job.result = { name: "界面测试文件", bytes: job.total };
|
||||
|
||||
@@ -43,6 +43,10 @@ async function app(t) {
|
||||
};
|
||||
const storages = [{ id: 1, name: "内部存储", free: 1e9, capacity: 2e9 }];
|
||||
const model = {
|
||||
hdcAvailable: false,
|
||||
hdcConnected: true,
|
||||
confirmed: false,
|
||||
parameters: [],
|
||||
attached: true,
|
||||
connection: null,
|
||||
calls: [],
|
||||
@@ -57,6 +61,7 @@ async function app(t) {
|
||||
`@self.${method}`,
|
||||
async (params) => {
|
||||
model.calls.push(method);
|
||||
model.parameters.push([method, structuredClone(params)]);
|
||||
return structuredClone(await fn(params));
|
||||
},
|
||||
];
|
||||
@@ -68,6 +73,45 @@ async function app(t) {
|
||||
};
|
||||
window.__TROVE_BRIDGE__ = createMockBridge(
|
||||
Object.fromEntries([
|
||||
handler("hdc.status", () => ({ busy: false, job: null })),
|
||||
handler("hdc.tool", () => ({
|
||||
available: model.hdcAvailable,
|
||||
version: "HDC test",
|
||||
path: "/fixture/hdc",
|
||||
})),
|
||||
handler("hdc.devices", () => ({
|
||||
devices: model.hdcConnected
|
||||
? [{ id: "phone", transport: "USB", state: "Connected" }]
|
||||
: [],
|
||||
})),
|
||||
handler("hdc.inspect", () => ({
|
||||
capabilities: Object.fromEntries(
|
||||
["apps", "launch", "logs", "report", "screenshot"].map((key) => [
|
||||
key,
|
||||
{ available: true },
|
||||
]),
|
||||
),
|
||||
info: { text: "Test device" },
|
||||
})),
|
||||
handler("hdc.apps", () => ({ apps: ["com.example.app"] })),
|
||||
handler("hdc.app", () => ({
|
||||
bundle: "com.example.app",
|
||||
versionCode: 1,
|
||||
versionName: "1.0",
|
||||
debug: false,
|
||||
abilities: [{ name: "Home", module: "entry" }],
|
||||
details: {},
|
||||
})),
|
||||
handler("hdc.package", () => ({
|
||||
bundle: "com.example.app",
|
||||
versionCode: 2,
|
||||
versionName: "2.0",
|
||||
sha256: "confirmed-sha256",
|
||||
size: 100,
|
||||
})),
|
||||
handler("hdc.start", () => ({ id: 9 })),
|
||||
["host.dialog.confirm", () => ({ confirmed: model.confirmed })],
|
||||
["host.dialog.openDirectory", () => ({ path: "/fixture/folder" })],
|
||||
handler("info", () => ({ connection: model.connection, busy: false })),
|
||||
handler("devices", () => ({ devices: model.attached ? [device] : [] })),
|
||||
handler("connect", () => (model.connection = { device, storages })),
|
||||
@@ -257,3 +301,67 @@ test("manual disconnect keeps the UI responsive while USB cleanup is pending", a
|
||||
assert.equal($("connect").disabled, false);
|
||||
assert.equal($("browser").hidden, true);
|
||||
});
|
||||
|
||||
test("whole folder upload uses the directory picker and exposes transfer metrics", async (t) => {
|
||||
const { $, click, connect, model, tick } = await app(t);
|
||||
await connect();
|
||||
await click("upload-folder");
|
||||
assert.equal(
|
||||
model.parameters.find(([name]) => name === "transfer")[1].path,
|
||||
"/fixture/folder",
|
||||
);
|
||||
model.job.statistics = {
|
||||
bytesPerSecond: 1024,
|
||||
averageBytesPerSecond: 1024,
|
||||
elapsedSeconds: 2,
|
||||
remainingSeconds: 3,
|
||||
filesCompleted: 1,
|
||||
filesTotal: 4,
|
||||
currentFile: "nested/data.bin",
|
||||
};
|
||||
await tick();
|
||||
assert.match($("transfer-current").textContent, /nested\/data.bin/);
|
||||
assert.match($("transfer-metrics").textContent, /KB\/s/);
|
||||
});
|
||||
test("management is optional and missing HDC does not disable file transfers", async (t) => {
|
||||
const { $, click, connect, model } = await app(t);
|
||||
assert.ok(!model.calls.some((name) => name.startsWith("hdc.")));
|
||||
await click("tab-management");
|
||||
assert.match($("hdc-tool-status").textContent, /未发现/);
|
||||
assert.equal($("app-install").disabled, true);
|
||||
await click("tab-files");
|
||||
await connect();
|
||||
assert.equal($("browser").hidden, false);
|
||||
});
|
||||
test("application installation requires confirmation and pins the reviewed package hash", async (t) => {
|
||||
const { $, click, model } = await app(t);
|
||||
model.hdcAvailable = true;
|
||||
await click("tab-management");
|
||||
await click("app-install");
|
||||
assert.ok(!model.calls.includes("hdc.start"));
|
||||
model.confirmed = true;
|
||||
await click("app-install");
|
||||
const params = model.parameters.find(([name]) => name === "hdc.start")[1];
|
||||
assert.deepEqual(params, {
|
||||
kind: "install",
|
||||
target: "phone",
|
||||
path: "/fixture/file.txt",
|
||||
sha256: "confirmed-sha256",
|
||||
});
|
||||
assert.equal($("app-install").disabled, true);
|
||||
});
|
||||
test("device disconnect clears app details and prevents stale target operations", async (t) => {
|
||||
const { $, click, model } = await app(t);
|
||||
model.hdcAvailable = true;
|
||||
await click("tab-management");
|
||||
$("apps-list").querySelector("button").click();
|
||||
await settle();
|
||||
assert.equal($("app-detail").hidden, false);
|
||||
assert.equal($("app-export").disabled, true);
|
||||
model.hdcConnected = false;
|
||||
t.mock.timers.tick(3500);
|
||||
await settle();
|
||||
assert.equal($("app-detail").hidden, true);
|
||||
assert.equal($("app-install").disabled, true);
|
||||
assert.equal($("app-launch").disabled, true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user