xref: /aosp_15_r20/external/pigweed/ts/transport/web_serial_transport_test.ts (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1// Copyright 2022 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15/* eslint-env browser */
16import { last, take } from 'rxjs/operators';
17
18import { SerialMock } from './serial_mock';
19import { WebSerialTransport, DeviceLockedError } from './web_serial_transport';
20import type { Serial } from 'pigweedjs/types/serial';
21
22describe('WebSerialTransport', () => {
23  let serialMock: SerialMock;
24  beforeEach(() => {
25    serialMock = new SerialMock();
26  });
27
28  it('is disconnected before connecting', () => {
29    const transport = new WebSerialTransport(serialMock as Serial);
30    expect(transport.connected.getValue()).toBe(false);
31  });
32
33  it('reports that it has connected', async () => {
34    const transport = new WebSerialTransport(serialMock as Serial);
35    await transport.connect();
36    expect(serialMock.serialPort.open).toHaveBeenCalled();
37    expect(transport.connected.getValue()).toBe(true);
38  });
39
40  it('emits chunks as they arrive from the device', async () => {
41    const transport = new WebSerialTransport(serialMock as Serial);
42    await transport.connect();
43    const data = new Uint8Array([1, 2, 3]);
44    const emitted = transport.chunks.pipe(take(1)).toPromise();
45    serialMock.dataFromDevice(data);
46
47    expect(await emitted).toEqual(data);
48    expect(transport.connected.getValue()).toBe(true);
49    expect(serialMock.serialPort.readable.locked).toBe(true);
50    expect(serialMock.serialPort.writable.locked).toBe(true);
51  });
52
53  it('is disconnected when it reaches the final chunk', async () => {
54    const transport = new WebSerialTransport(serialMock as Serial);
55    await transport.connect();
56    const disconnectPromise = transport.connected
57      .pipe(take(2), last())
58      .toPromise();
59    serialMock.closeFromDevice();
60
61    expect(await disconnectPromise).toBe(false);
62  });
63
64  it('waits for the writer to be ready', async () => {
65    const transport = new WebSerialTransport(serialMock as Serial);
66    await transport.connect();
67    const data = new Uint8Array([1, 2, 3]);
68
69    const dataToDevice = serialMock.dataToDevice.pipe(take(1)).toPromise();
70
71    let writtenData: Uint8Array | undefined = undefined;
72    dataToDevice.then((data) => {
73      writtenData = data;
74    });
75
76    const sendPromise = transport.sendChunk(data);
77    expect(writtenData).toBeUndefined();
78    await sendPromise;
79    expect(writtenData).toBeDefined();
80  });
81
82  it('sends chunks to the device', async () => {
83    const transport = new WebSerialTransport(serialMock as Serial);
84    await transport.connect();
85    const data = new Uint8Array([1, 2, 3]);
86
87    const dataToDevice = serialMock.dataToDevice.pipe(take(1)).toPromise();
88
89    await transport.sendChunk(data);
90    expect(await dataToDevice).toEqual(data);
91  });
92
93  it('throws an error on failing to connect', async () => {
94    const connectError = new Error('Example connection error');
95    const spy = jest.spyOn(serialMock, 'requestPort').mockImplementation(() => {
96      throw connectError;
97    });
98    const transport = new WebSerialTransport(serialMock as Serial);
99    await expect(transport.connect()).rejects.toThrow(connectError.message);
100  });
101
102  it("emits connection errors in the 'errors' observable", async () => {
103    const transport = new WebSerialTransport(serialMock as Serial);
104    await transport.connect();
105
106    const reportedErrorPromise = transport.errors.pipe(take(1)).toPromise();
107    serialMock.serialPort.errorFromDevice(new DeviceLockedError());
108
109    expect(await reportedErrorPromise).toEqual(new DeviceLockedError());
110  });
111});
112