actions: simplify usage of Matchable

This removes use of the pseudo-internal toString() function and also
removes extraneous uses of ReturnType<>. Also, a new when() method
is added to simplify additional uses.
This commit is contained in:
David Lechner
2022-02-26 12:43:10 -06:00
parent 3cbfc4ca68
commit a9f4eaa32d
13 changed files with 125 additions and 142 deletions
+14 -24
View File
@@ -37,51 +37,45 @@ describe('downloadAndRun', () => {
// first, it tries to compile the program in the current editor
const compileAction = await saga.take();
expect(compileAction.type).toBe(compile.toString());
expect(compile.matches(compileAction)).toBeTruthy();
saga.put(didCompile(new Uint8Array(30)));
// then it notifies that loading has begun
const loadingStatusAction = await saga.take();
expect(loadingStatusAction.type).toBe(didStartDownload.toString());
expect(loadingStatusAction).toEqual(didStartDownload());
// first message is the length
const writeAction = await saga.take();
expect(writeAction.type).toBe(write.toString());
expect(writeAction).toBeTruthy();
expect((writeAction as ReturnType<typeof write>).value.length).toBe(4);
saga.put(didWrite((writeAction as ReturnType<typeof write>).id));
saga.put(didWrite(0));
saga.put(checksum(30));
// then progress is updated
const progressAction = await saga.take();
expect(progressAction.type).toBe(didProgressDownload.toString());
expect(
(progressAction as ReturnType<typeof didProgressDownload>).progress,
).toBe(0);
expect(progressAction).toEqual(didProgressDownload(0));
// then the first chunk of 20 bytes
const writeAction2 = await saga.take();
expect(writeAction2.type).toBe(write.toString());
expect(write.matches(writeAction2)).toBeTruthy();
expect((writeAction2 as ReturnType<typeof write>).value.length).toBe(20);
saga.put(didWrite((writeAction2 as ReturnType<typeof write>).id));
saga.put(didWrite(1));
saga.put(checksum(0));
// then progress is updated
const progress2Action = await saga.take();
expect(progress2Action.type).toBe(didProgressDownload.toString());
expect(
(progress2Action as ReturnType<typeof didProgressDownload>).progress,
).toBe(20 / 30);
expect(progress2Action).toEqual(didProgressDownload(20 / 30));
// then last chunk
const writeAction3 = await saga.take();
expect(writeAction3.type).toBe(write.toString());
expect(write.matches(writeAction3)).toBeTruthy();
expect((writeAction3 as ReturnType<typeof write>).value.length).toBe(10);
saga.put(didWrite((writeAction3 as ReturnType<typeof write>).id));
saga.put(didWrite(2));
saga.put(checksum(0));
// Then a status message saying that we are done
const loadedStatusAction = await saga.take();
expect(loadedStatusAction.type).toBe(didFinishDownload.toString());
expect(loadedStatusAction).toEqual(didFinishDownload());
await saga.end();
});
@@ -95,7 +89,7 @@ test('repl', async () => {
saga.put(repl());
const action = await saga.take();
expect(action.type).toBe(write.toString());
expect(action).toEqual(write(0, new Uint8Array([32, 32, 32, 32])));
await saga.end();
});
@@ -106,13 +100,9 @@ test('stop', async () => {
saga.put(stop());
const pybricksServiceAction = await saga.take();
expect(pybricksServiceAction.type).toBe(sendStopUserProgramCommand.toString());
expect(pybricksServiceAction).toEqual(sendStopUserProgramCommand(0));
saga.put(
didSendCommand(
(pybricksServiceAction as ReturnType<typeof sendStopUserProgramCommand>).id,
),
);
saga.put(didSendCommand(0));
await saga.end();
});
+7 -16
View File
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
import { AnyAction } from 'redux';
import {
SagaGenerator,
actionChannel,
@@ -43,12 +42,8 @@ function* waitForWrite(id: number): SagaGenerator<{
didFailToWrite: ReturnType<typeof didFailToWrite> | undefined;
}> {
return yield* race({
didWrite: take<ReturnType<typeof didWrite>>(
(a: AnyAction) => didWrite.matches(a) && a.id === id,
),
didFailToWrite: take<ReturnType<typeof didFailToWrite>>(
(a: AnyAction) => didFailToWrite.matches(a) && a.id === id,
),
didWrite: take(didWrite.when((a) => a.id === id)),
didFailToWrite: take(didFailToWrite.when((a) => a.id === id)),
});
}
@@ -64,8 +59,8 @@ function* handleDownloadAndRun(): Generator {
const script = editor.getValue();
yield* put(compile(script, ['-mno-unicode']));
const { mpy, mpyFail } = yield* race({
mpy: take<ReturnType<typeof didCompile>>(didCompile),
mpyFail: take<ReturnType<typeof didFailToCompile>>(didFailToCompile),
mpy: take(didCompile),
mpyFail: take(didFailToCompile),
});
if (mpyFail) {
@@ -82,7 +77,7 @@ function* handleDownloadAndRun(): Generator {
console.log(`Downloading ${mpy.data.byteLength} bytes`);
}
const checksumChannel = yield* actionChannel<ReturnType<typeof checksum>>(checksum);
const checksumChannel = yield* actionChannel(checksum);
const nextMessageId = yield* getContext<() => number>('nextMessageId');
@@ -180,12 +175,8 @@ function* handleStop(): Generator {
// REVISIT: may want to disable button while attempting to send command
// this would mean didSendStop() and didFailToSendStop() actions here
const { failedToSend } = yield* race({
sent: take<ReturnType<typeof didSendCommand>>(
(a: AnyAction) => didSendCommand.matches(a) && a.id === id,
),
failedToSend: take<ReturnType<typeof didFailToSendCommand>>(
(a: AnyAction) => didFailToSendCommand.matches(a) && a.id === id,
),
sent: take(didSendCommand.when((a) => a.id === id)),
failedToSend: take(didFailToSendCommand.when((a) => a.id === id)),
});
if (failedToSend) {
// TODO: probably want to check error. If hub disconnected, ignore error