mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-07-28 04:08:05 +00:00
convert editor service to saga
This commit is contained in:
committed by
David Lechner
parent
5c3ec89e3b
commit
17cb5a32d5
@@ -4,12 +4,39 @@
|
||||
import { Ace } from 'ace-builds';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { AsyncSaga } from '../../test';
|
||||
import { reloadProgram } from '../actions/editor';
|
||||
import { open, reloadProgram, saveAs } from '../actions/editor';
|
||||
import editor from './editor';
|
||||
|
||||
jest.mock('ace-builds');
|
||||
jest.mock('file-saver');
|
||||
|
||||
test('open', async () => {
|
||||
const saga = new AsyncSaga(editor);
|
||||
const mockEditor = mock<Ace.EditSession>();
|
||||
const data = new Uint8Array().buffer;
|
||||
|
||||
saga.setState({ editor: { current: mockEditor } });
|
||||
saga.put(open(data));
|
||||
|
||||
expect(mockEditor.setValue).toBeCalled();
|
||||
|
||||
await saga.end();
|
||||
});
|
||||
|
||||
test('saveAs', async () => {
|
||||
const saga = new AsyncSaga(editor);
|
||||
const mockEditor = mock<Ace.EditSession>();
|
||||
|
||||
saga.setState({ editor: { current: mockEditor } });
|
||||
saga.put(saveAs());
|
||||
|
||||
expect(mockEditor.getValue).toBeCalled();
|
||||
|
||||
await saga.end();
|
||||
});
|
||||
|
||||
test('reloadProgram', async () => {
|
||||
const saga = new AsyncSaga(editor);
|
||||
|
||||
const mockEditor = mock<Ace.EditSession>();
|
||||
|
||||
saga.setState({ editor: { current: mockEditor } });
|
||||
|
||||
+51
-2
@@ -2,18 +2,67 @@
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { Ace } from 'ace-builds';
|
||||
import FileSaver from 'file-saver';
|
||||
import { select, takeEvery } from 'redux-saga/effects';
|
||||
import { EditorActionType, EditorReloadProgramAction } from '../actions/editor';
|
||||
import {
|
||||
EditorActionType,
|
||||
EditorOpenAction,
|
||||
EditorReloadProgramAction,
|
||||
EditorSaveAsAction,
|
||||
} from '../actions/editor';
|
||||
import { RootState } from '../reducers';
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
function* open(action: EditorOpenAction): Generator {
|
||||
const editor = (yield select(
|
||||
(s: RootState) => s.editor.current,
|
||||
)) as Ace.EditSession | null;
|
||||
|
||||
// istanbul ignore next: it is a bug to dispatch this action with no current editor
|
||||
if (editor === null) {
|
||||
console.error('open: No current editor');
|
||||
return;
|
||||
}
|
||||
|
||||
const text = decoder.decode(action.data);
|
||||
editor.setValue(text);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function* saveAs(action: EditorSaveAsAction): Generator {
|
||||
const editor = (yield select(
|
||||
(s: RootState) => s.editor.current,
|
||||
)) as Ace.EditSession | null;
|
||||
|
||||
// istanbul ignore next: it is a bug to dispatch this action with no current editor
|
||||
if (editor === null) {
|
||||
console.error('saveAs: No current editor');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = editor.getValue();
|
||||
const blob = new Blob([data], { type: 'text/x-python;charset=utf-8' });
|
||||
FileSaver.saveAs(blob, 'main.py');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function* reloadProgram(_action: EditorReloadProgramAction): Generator {
|
||||
const editor = (yield select(
|
||||
(s: RootState) => s.editor.current,
|
||||
)) as Ace.EditSession;
|
||||
)) as Ace.EditSession | null;
|
||||
|
||||
// istanbul ignore next: it is a bug to dispatch this action with no current editor
|
||||
if (editor === null) {
|
||||
console.error('reloadProgram: No current editor');
|
||||
return;
|
||||
}
|
||||
|
||||
editor.setValue(localStorage.getItem('program') || '');
|
||||
}
|
||||
|
||||
export default function* (): Generator {
|
||||
yield takeEvery(EditorActionType.Open, open);
|
||||
yield takeEvery(EditorActionType.SaveAs, saveAs);
|
||||
yield takeEvery(EditorActionType.ReloadProgram, reloadProgram);
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import * as FileSaver from 'file-saver';
|
||||
import { Action, Dispatch } from '../actions';
|
||||
import { EditorActionType, EditorOpenAction } from '../actions/editor';
|
||||
import { RootState } from '../reducers';
|
||||
import { combineServices } from '.';
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
function open(action: Action, _dispatch: Dispatch, state: RootState): void {
|
||||
if (action.type !== EditorActionType.Open) {
|
||||
return;
|
||||
}
|
||||
// istanbul ignore next: currently, it is a bug if there is no current editor
|
||||
if (state.editor.current === null) {
|
||||
console.error('No current editor');
|
||||
return;
|
||||
}
|
||||
const text = decoder.decode((action as EditorOpenAction).data);
|
||||
state.editor.current.getDocument().setValue(text);
|
||||
}
|
||||
|
||||
function saveAs(action: Action, _dispatch: Dispatch, state: RootState): void {
|
||||
if (action.type !== EditorActionType.SaveAs) {
|
||||
return;
|
||||
}
|
||||
// istanbul ignore next: currently, it is a bug if there is no current editor
|
||||
if (state.editor.current === null) {
|
||||
console.error('No current editor');
|
||||
return;
|
||||
}
|
||||
const data = state.editor.current.getDocument().getValue();
|
||||
const blob = new Blob([data], { type: 'text/x-python;charset=utf-8' });
|
||||
FileSaver.saveAs(blob, 'main.py');
|
||||
}
|
||||
|
||||
export default combineServices(open, saveAs);
|
||||
@@ -5,7 +5,6 @@ import { Middleware } from 'redux';
|
||||
import { Action, Dispatch } from '../actions';
|
||||
import { RootState } from '../reducers';
|
||||
import ble from './ble';
|
||||
import editor from './editor';
|
||||
import errorLog from './error-log';
|
||||
import hub from './hub';
|
||||
import bootloader from './lwp3-bootloader';
|
||||
@@ -39,7 +38,7 @@ export function combineServices(...services: Service[]): Service {
|
||||
};
|
||||
}
|
||||
|
||||
const rootService = combineServices(ble, bootloader, editor, errorLog, hub);
|
||||
const rootService = combineServices(ble, bootloader, errorLog, hub);
|
||||
|
||||
const serviceMiddleware: Middleware = (store) => (next) => (action): unknown => {
|
||||
runService(rootService, action, store.dispatch, store.getState());
|
||||
|
||||
+5
-1
@@ -1,12 +1,16 @@
|
||||
const Environment = require('jest-environment-jsdom');
|
||||
|
||||
/**
|
||||
* A custom environment to set the TextEncoder
|
||||
* A custom environment to set TextDecoder/TextEncoder
|
||||
* Thanks https://stackoverflow.com/a/57713960/1976323
|
||||
*/
|
||||
module.exports = class CustomTestEnvironment extends Environment {
|
||||
async setup() {
|
||||
await super.setup();
|
||||
if (typeof TextDecoder === 'undefined') {
|
||||
const { TextDecoder } = require('util');
|
||||
this.global.TextDecoder = TextDecoder;
|
||||
}
|
||||
if (typeof TextEncoder === 'undefined') {
|
||||
const { TextEncoder } = require('util');
|
||||
this.global.TextEncoder = TextEncoder;
|
||||
|
||||
Reference in New Issue
Block a user