1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
15 /* eslint-env browser, jasmine */
16 import {last, take} from 'rxjs/operators';
17 import {SerialMock} from './serial_mock';
18 import {WebSerialTransport, DeviceLockedError} from './web_serial_transport';
20 describe('WebSerialTransport', () => {
21 let serialMock: SerialMock;
23 serialMock = new SerialMock();
26 it('is disconnected before connecting', () => {
27 const transport = new WebSerialTransport(serialMock as Serial);
28 expect(transport.connected.getValue()).toBeFalse();
31 it('reports that it has connected', async () => {
32 const transport = new WebSerialTransport(serialMock as Serial);
33 await transport.connect();
34 expect(serialMock.serialPort.open).toHaveBeenCalled();
35 expect(transport.connected.getValue()).toBeTrue();
38 it('emits chunks as they arrive from the device', async () => {
39 const transport = new WebSerialTransport(serialMock as Serial);
40 await transport.connect();
41 const data = new Uint8Array([1, 2, 3]);
42 const emitted = transport.chunks.pipe(take(1)).toPromise();
43 serialMock.dataFromDevice(data);
45 expect(await emitted).toEqual(data);
46 expect(transport.connected.getValue()).toBeTrue();
47 expect(serialMock.serialPort.readable.locked).toBeTrue();
48 expect(serialMock.serialPort.writable.locked).toBeTrue();
51 it('is disconnected when it reaches the final chunk', async () => {
52 const transport = new WebSerialTransport(serialMock as Serial);
53 await transport.connect();
54 const disconnectPromise = transport.connected
55 .pipe(take(2), last())
57 serialMock.closeFromDevice();
59 expect(await disconnectPromise).toBeFalse();
62 it('waits for the writer to be ready', async () => {
63 const transport = new WebSerialTransport(serialMock as Serial);
64 await transport.connect();
65 const data = new Uint8Array([1, 2, 3]);
67 const dataToDevice = serialMock.dataToDevice.pipe(take(1)).toPromise();
69 let writtenData: Uint8Array | undefined = undefined;
70 dataToDevice.then(data => {
74 const sendPromise = transport.sendChunk(data);
75 expect(writtenData).toBeUndefined();
77 expect(writtenData).toBeDefined();
80 it('sends chunks to the device', async () => {
81 const transport = new WebSerialTransport(serialMock as Serial);
82 await transport.connect();
83 const data = new Uint8Array([1, 2, 3]);
85 const dataToDevice = serialMock.dataToDevice.pipe(take(1)).toPromise();
87 await transport.sendChunk(data);
88 expect(await dataToDevice).toEqual(data);
91 it('throws an error on failing to connect', async () => {
92 const connectError = new Error('Example connection error');
93 spyOn(serialMock, 'requestPort').and.throwError(connectError);
94 const transport = new WebSerialTransport(serialMock as Serial);
95 await expectAsync(transport.connect()).toBeRejectedWith(connectError);
98 it("emits connection errors in the 'errors' observable", async () => {
99 const transport = new WebSerialTransport(serialMock as Serial);
100 await transport.connect();
102 const reportedErrorPromise = transport.errors.pipe(take(1)).toPromise();
103 serialMock.serialPort.errorFromDevice(new Error());
105 expect(await reportedErrorPromise).toEqual(new DeviceLockedError());