Files
meshtastic-web/tests/utils/transportContract.ts
T
Dan DitomasoandGitHub c729d3b25e fix: resolve lint warnings/errors and apply formatting (#1024)
* fix: resolve lint warnings/errors and apply formatting

Fix 10 oxlint issues (2 errors, 8 warnings):
- Remove unused catch parameters in Security.tsx and ImportDialog.tsx
- Remove stray expression in Generator.tsx
- Add eslint-disable for debounced useCallback in FilterControl.tsx
- Remove unnecessary deps (resolveDB, store) in bindStoreToDevice.ts
- Prefix unused variant param in AppSidebar.tsx
- Memoize tabs arrays in DeviceConfig, RadioConfig, ModuleConfig
- Fix channels type in RadioConfig TabItem

Also applies oxfmt formatting across all files.

* updating lock file

* update protobuf package

* fix: regenerate pnpm-lock.yaml and exclude jsr protobufs from minimumReleaseAge

* prevented http card from always being polled

* updated pnpm config fiile

* updated actions

* removed biome config and lint system leftovers

* updating protobuf package
2026-03-11 21:54:25 -04:00

119 lines
3.5 KiB
TypeScript

import { Types } from "@meshtastic/core";
import { describe, expect, it } from "vitest";
export interface TransportContract {
name: string;
create: () => Promise<Types.Transport>;
setup?: () => void | Promise<void>;
teardown?: () => void | Promise<void>;
pushIncoming?: (bytes: Uint8Array) => void | Promise<void>;
assertLastWritten?: (bytes: Uint8Array) => void;
triggerDisconnect?: () => void | Promise<void>;
}
async function readUntilType(
reader: ReadableStreamDefaultReader<Types.DeviceOutput>,
expectedType: Types.DeviceOutput["type"],
maxReads = 20,
): Promise<Types.DeviceOutput> {
for (let i = 0; i < maxReads; i++) {
const { value, done } = await reader.read();
if (done) {
break;
}
if (value && value.type === expectedType) {
return value;
}
}
throw new Error(`Did not receive a '${expectedType}' event within ${maxReads} reads`);
}
export function runTransportContract(contract: TransportContract) {
describe(contract.name, () => {
it("reads packets from fromDevice", async () => {
await contract.setup?.();
const transport = await contract.create();
const reader = transport.fromDevice.getReader();
const sampleBytes = new Uint8Array([0x01, 0x02, 0x03]);
await contract.pushIncoming?.(sampleBytes);
const packetEvent = await readUntilType(reader, "packet");
expect("data" in packetEvent ? packetEvent.data : undefined).toEqual(sampleBytes);
reader.releaseLock();
await contract.teardown?.();
});
it("writes bytes to toDevice", async () => {
await contract.setup?.();
const transport = await contract.create();
const writer = transport.toDevice.getWriter();
const outgoingBytes = new Uint8Array([0xaa, 0xbb]);
await writer.write(outgoingBytes);
await writer.close();
contract.assertLastWritten?.(outgoingBytes);
await contract.teardown?.();
});
it("disconnect() emits DeviceDisconnected('user')", async () => {
await contract.setup?.();
const transport = await contract.create();
const reader = transport.fromDevice.getReader();
// Trigger user disconnect
await transport.disconnect();
// Read a few events and assert we eventually see the user disconnect.
let sawUser = false;
for (let i = 0; i < 10; i++) {
const { value } = await reader.read();
if (
value &&
value.type === "status" &&
value.data.status === Types.DeviceStatusEnum.DeviceDisconnected &&
value.data.reason === "user"
) {
sawUser = true;
break;
}
}
expect(sawUser).toBe(true);
reader.releaseLock();
await contract.teardown?.();
});
it("emits DeviceDisconnected when the underlying link drops", async () => {
await contract.setup?.();
const transport = await contract.create();
const reader = transport.fromDevice.getReader();
await contract.triggerDisconnect?.();
// As above, read a few events and assert we eventually see "disconnected"
let sawDrop = false;
for (let i = 0; i < 10; i++) {
const { value } = await reader.read();
if (
value &&
value.type === "status" &&
value.data.status === Types.DeviceStatusEnum.DeviceDisconnected
) {
sawDrop = true;
break;
}
}
expect(sawDrop).toBe(true);
reader.releaseLock();
await contract.teardown?.();
});
});
}