From 1e0d44bbe4429481948d23d6932dcfabcb2060fb Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 6 Jul 2022 15:56:22 -0500 Subject: [PATCH] Fixed deleting files that are not open in the editor. --- CHANGELOG.md | 3 ++ src/explorer/sagas.test.ts | 59 ++++++++++++++++++++++++-------------- src/explorer/sagas.ts | 11 +++++-- test/index.tsx | 9 ++---- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7af21350..3b07d63c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ## [Unreleased] +### Fixed +- Fixed deleting files that are not open in the editor. + ## [2.0.0-beta.3] - 2022-07-06 ### Changed diff --git a/src/explorer/sagas.test.ts b/src/explorer/sagas.test.ts index 66de7c1f..19726eea 100644 --- a/src/explorer/sagas.test.ts +++ b/src/explorer/sagas.test.ts @@ -14,6 +14,7 @@ import { editorDidFailToActivateFile, } from '../editor/actions'; import { EditorError } from '../editor/error'; +import { UUID } from '../fileStorage'; import { fileStorageCopyFile, fileStorageDeleteFile, @@ -471,31 +472,47 @@ describe('handleExplorerDeleteFile', () => { ); }); - describe('accepted', () => { - beforeEach(async () => { - saga.put(deleteFileAlertDidAccept()); + describe.each([false, true])( + 'accepted, file is open: %o', + (fileIsOpenInEditor: boolean) => { + beforeEach(async () => { + if (fileIsOpenInEditor) { + const openFileUuids: readonly UUID[] = [uuid(0)]; + saga.updateState({ editor: { openFileUuids } }); + } - // should close the editor first - await expect(saga.take()).resolves.toEqual(editorCloseFile(uuid(0))); - saga.put(editorDidCloseFile(uuid(0))); + saga.put(deleteFileAlertDidAccept()); - // then delete the file - await expect(saga.take()).resolves.toEqual(fileStorageDeleteFile(testFile)); - }); + if (fileIsOpenInEditor) { + // should close the editor first + await expect(saga.take()).resolves.toEqual( + editorCloseFile(uuid(0)), + ); + saga.put(editorDidCloseFile(uuid(0))); + } - it('should propagate error', async () => { - const testError = new Error('test error'); - saga.put(fileStorageDidFailToDeleteFile(testFile, testError)); - await expect(saga.take()).resolves.toEqual( - explorerDidFailToDeleteFile(testFile, testError), - ); - }); + // then delete the file + await expect(saga.take()).resolves.toEqual( + fileStorageDeleteFile(testFile), + ); + }); - it('should succeed', async () => { - saga.put(fileStorageDidDeleteFile(testFile)); - await expect(saga.take()).resolves.toEqual(explorerDidDeleteFile(testFile)); - }); - }); + it('should propagate error', async () => { + const testError = new Error('test error'); + saga.put(fileStorageDidFailToDeleteFile(testFile, testError)); + await expect(saga.take()).resolves.toEqual( + explorerDidFailToDeleteFile(testFile, testError), + ); + }); + + it('should succeed', async () => { + saga.put(fileStorageDidDeleteFile(testFile)); + await expect(saga.take()).resolves.toEqual( + explorerDidDeleteFile(testFile), + ); + }); + }, + ); afterEach(async () => { await saga.end(); diff --git a/src/explorer/sagas.ts b/src/explorer/sagas.ts index 3cacef3a..84602c6c 100644 --- a/src/explorer/sagas.ts +++ b/src/explorer/sagas.ts @@ -3,7 +3,7 @@ import { fileOpen, fileSave } from 'browser-fs-access'; import JSZip from 'jszip'; -import { call, put, race, take, takeEvery } from 'typed-redux-saga/macro'; +import { call, put, race, select, take, takeEvery } from 'typed-redux-saga/macro'; import { alertsShowAlert } from '../alerts/actions'; import { editorActivateFile, @@ -41,6 +41,7 @@ import { pythonFileMimeType, validateFileName, } from '../pybricksMicropython/lib'; +import { RootState } from '../reducers'; import { defined, ensureError, timestamp } from '../utils'; import { explorerArchiveAllFiles, @@ -405,9 +406,13 @@ function* handleExplorerDeleteFile(action: ReturnType // at this point we know the user accepted + const openUuids = yield* select((s: RootState) => s.editor.openFileUuids); + // have to close editor before deleting, otherwise we get "in use" error - yield* put(editorCloseFile(action.uuid)); - yield* take(editorDidCloseFile.when((a) => a.uuid === action.uuid)); + if (openUuids.includes(action.uuid)) { + yield* put(editorCloseFile(action.uuid)); + yield* take(editorDidCloseFile.when((a) => a.uuid === action.uuid)); + } yield* put(fileStorageDeleteFile(action.fileName)); diff --git a/test/index.tsx b/test/index.tsx index a4f6d9ba..a8fe20e9 100644 --- a/test/index.tsx +++ b/test/index.tsx @@ -9,12 +9,7 @@ import userEvent from '@testing-library/user-event'; import type { UserEvent } from '@testing-library/user-event/dist/types/setup'; import React, { ReactElement } from 'react'; import { Provider } from 'react-redux'; -import { - AnyAction, - DeepPartial, - PreloadedState, - legacy_createStore as createStore, -} from 'redux'; +import { AnyAction, PreloadedState, legacy_createStore as createStore } from 'redux'; import { END, MulticastChannel, Saga, Task, runSaga, stdChannel } from 'redux-saga'; import { UUID } from '../src/fileStorage'; import { RootState, rootReducer } from '../src/reducers'; @@ -81,7 +76,7 @@ export class AsyncSaga { return Promise.resolve(next); } - public updateState(state: DeepPartial): void { + public updateState(state: PreloadedState): void { for (const key of Object.keys(state) as Array) { // @ts-expect-error: writing to readonly for testing this.state[key] = { ...this.state[key], ...state[key] };