diff --git a/package.json b/package.json index ad49e59..e13c094 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "build": "tsc", "prepack": "npm run build", "pretest": "npm run build", - "test": "mocha -r ts-node/register 'test/**/*.spec.ts'" + "test": "npm run test:unit && npm run test:integration", + "test:unit": "mocha -r ts-node/register 'test/unit-tests.spec.ts'", + "test:integration": "mocha -r ts-node/register 'test/integration-tests.spec.ts'" }, "repository": { "type": "git", @@ -34,6 +36,7 @@ "@types/mocha": "^10.0.6", "@types/plist": "^3.0.5", "chai": "^4.4.1", + "destroyable-server": "^1.0.2", "mocha": "^10.4.0", "rimraf": "^5.0.7", "ts-node": "^10.9.2", diff --git a/src/index.ts b/src/index.ts index 3b22a71..6850e4b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -98,7 +98,7 @@ class UsbmuxClient { this.listenToMessages(socket) } - private deviceData: Record> = {}; + private deviceData: Record> = {}; // Listen for events by using readMessageFromStream in an async iterator: async listenToMessages(socket: net.Socket) { diff --git a/test/integration-tests.spec.ts b/test/integration-tests.spec.ts new file mode 100644 index 0000000..eb426dd --- /dev/null +++ b/test/integration-tests.spec.ts @@ -0,0 +1,48 @@ +import { expect } from "chai"; +import { delay } from "@httptoolkit/util"; + +import { UsbmuxClient, getUsbmuxClient } from "../src/index"; + +describe("Usbmux-client integration tests", () => { + + let client: UsbmuxClient | undefined; + + afterEach(() => { + client?.close(); + client = undefined; + }); + + if (process.env.CI) { + it("can query the connected devices (seeing no results)", async () => { + client = await getUsbmuxClient(); + await delay(10); // Not clear this is necessary, but not unhelpful + + const devices = client.getDevices(); + + // Tests assume no devices in CI but this at least confirms we can connect + // to Usbmux successfully. + expect(devices).to.deep.equal({}); + }); + } else { + it("can detect the connected device", async () => { + client = await getUsbmuxClient(); + await delay(10); // Not clear this is necessary, but not unhelpful + + const devices = client.getDevices(); + + // Local integratiion testing assumes you'll test with a real device + expect(devices).to.be.an('object'); + expect(Object.keys(devices).length).to.be.greaterThan(0, + "Local integration testing assumes at least one iOS device connected" + ); + + const deviceId = Object.keys(devices)[0]; + expect(devices[deviceId]).to.be.an('object'); + expect(devices[deviceId].DeviceID).to.equal(parseInt(deviceId)); + expect(devices[deviceId].ConnectionType).to.equal('USB'); + expect(devices[deviceId].SerialNumber).to.be.a('string'); + expect((devices[deviceId].SerialNumber as any).length).to.equal(40); + }); + } + +}); \ No newline at end of file diff --git a/test/test.spec.ts b/test/test.spec.ts deleted file mode 100644 index f7f6c2d..0000000 --- a/test/test.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { expect } from "chai"; -import { delay } from "@httptoolkit/util"; - -import { UsbmuxClient, getUsbmuxClient } from "../src/index"; - -describe("Usbmux-client", () => { - - let client: UsbmuxClient | undefined; - - afterEach(() => { - client?.close(); - client = undefined; - }) - - it("can expose the connected devices ", async () => { - client = await getUsbmuxClient(); - await delay(10); // Not clear this is necessary, but not unhelpful - - const devices = client.getDevices(); - - // Tests assume no devices (since this will always be the state in - // the CI environment) but this at least confirms we can connect - // to Usbmux successfully. - expect(devices).to.deep.equal({}); - }); - -}); \ No newline at end of file diff --git a/test/unit-tests.spec.ts b/test/unit-tests.spec.ts new file mode 100644 index 0000000..18804c4 --- /dev/null +++ b/test/unit-tests.spec.ts @@ -0,0 +1,48 @@ +import * as net from 'net'; + +import { expect } from 'chai'; +import { makeDestroyable } from "destroyable-server"; + +import { UsbmuxClient, getUsbmuxClient } from "../src/index"; +import { delay } from '@httptoolkit/util'; + +// Various Base64 encoded messages we expect or use a test data: +const MESSAGES = { + LISTEN_REQUEST: Buffer.from("hwEAAAAAAAAIAAAAAQAAADw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+CjwhRE9DVFlQRSBwbGlzdCBQVUJMSUMgIi0vL0FwcGxlLy9EVEQgUExJU1QgMS4wLy9FTiIgImh0dHA6Ly93d3cuYXBwbGUuY29tL0RURHMvUHJvcGVydHlMaXN0LTEuMC5kdGQiPgo8cGxpc3QgdmVyc2lvbj0iMS4wIj4KICA8ZGljdD4KICAgIDxrZXk+TWVzc2FnZVR5cGU8L2tleT4KICAgIDxzdHJpbmc+TGlzdGVuPC9zdHJpbmc+CiAgICA8a2V5PkNsaWVudFZlcnNpb25TdHJpbmc8L2tleT4KICAgIDxzdHJpbmc+dXNibXV4LWNsaWVudDwvc3RyaW5nPgogICAgPGtleT5Qcm9nTmFtZTwva2V5PgogICAgPHN0cmluZz51c2JtdXgtY2xpZW50PC9zdHJpbmc+CiAgPC9kaWN0Pgo8L3BsaXN0Pg==", "base64") +}; + +describe("Usbmux-client unit tests", () => { + + let serverSocket: net.Socket | undefined; + + const mockServer = makeDestroyable(net.createServer((socket) => { + if (serverSocket) serverSocket.destroy(); + serverSocket = socket; + })); + let mockServerPort: number | undefined; + + let client: UsbmuxClient | undefined; + + beforeEach(async () => { + mockServer.listen(0); + mockServerPort = (mockServer.address() as net.AddressInfo).port; + }); + + afterEach(async () => { + client?.close(); + client = undefined; + + await mockServer.destroy(); + serverSocket = undefined; + }); + + it.only("should send a hello message to start listening", async () => { + getUsbmuxClient({ port: mockServerPort! }); + + await delay(10); + const receivedData = serverSocket!.read(); + + expect(receivedData).to.deep.equal(MESSAGES.LISTEN_REQUEST); + }); + +}); \ No newline at end of file