Skip to content

Commit

Permalink
EDD-19: As a download app user, I want to be able to clear my downloa…
Browse files Browse the repository at this point in the history
…d history. (#28)

* EDD-19: Adds clearing download history on the downloadHistory page for the header button and individual actions
  • Loading branch information
eudoroolivares2016 authored Sep 13, 2023
1 parent ba9da56 commit 72cb052
Show file tree
Hide file tree
Showing 14 changed files with 302 additions and 15 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "earthdata-download",
"version": "0.0.19",
"version": "0.0.20",
"description": "Earthdata Download is a cross-platform download manager designed to improve how users download Earth Science data. It accepts lists of files from applications like Earthdata Search (https://search.earthdata.nasa.gov/) and enables clients to offer users a streamlined experience when downloading files from their browser.",
"repository": "nasa/earthdata-download",
"homepage": "https://github.com/nasa/earthdata-download#readme",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import React, { useContext } from 'react'
import { FaBan } from 'react-icons/fa'
import useAppContext from '../../hooks/useAppContext'
import { ElectronApiContext } from '../../context/ElectronApiContext'

import Button from '../Button/Button'

Expand All @@ -19,14 +20,13 @@ const DownloadHistoryHeader = () => {
const {
deleteAllToastsById
} = appContext
// TODO EDD-19
// const {
// clearDownloadHistory
// } = useContext(ElectronApiContext)

const {
clearDownloadHistory
} = useContext(ElectronApiContext)

const onClearDownloadHistory = () => {
// TODO EDD-19
// clearDownloadHistory()
clearDownloadHistory({})
deleteAllToastsById()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ describe('DownloadHistoryHeader component', () => {
const button = screen.getByRole('button', { name: 'Clear Download History' })
await userEvent.click(button)

expect(clearDownloadHistory).toHaveBeenCalledTimes(0)
expect(clearDownloadHistory).toHaveBeenCalledTimes(1)
expect(clearDownloadHistory).toHaveBeenCalledWith({})
expect(deleteAllToastsById).toHaveBeenCalledTimes(1)
})
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useContext } from 'react'
import PropTypes from 'prop-types'
import {
FaBan,
FaClipboard,
FaFolderOpen,
FaInfoCircle
Expand Down Expand Up @@ -42,6 +43,7 @@ const DownloadHistoryListItem = ({
deleteAllToastsById
} = appContext
const {
clearDownloadHistory,
copyDownloadPath,
openDownloadFolder,
restartDownload
Expand Down Expand Up @@ -90,13 +92,37 @@ const DownloadHistoryListItem = ({
restartDownload({ downloadId })
},
icon: FaInfoCircle
},
{
label: 'Clear Download',
isActive: true,
isPrimary: false,
callback: () => {
deleteAllToastsById(downloadId)
clearDownloadHistory({ downloadId })
},
icon: FaBan
}
]
]

const fetchLinksErroredActionsList = [
[
{
label: 'Clear Download',
isActive: true,
isPrimary: false,
callback: () => {
deleteAllToastsById(downloadId)
clearDownloadHistory({ downloadId })
},
icon: FaBan
}
], []]

return (
<DownloadItem
actionsList={shouldShowActions ? actionsList : null}
actionsList={shouldShowActions ? actionsList : fetchLinksErroredActionsList}
downloadId={downloadId}
showMoreInfoDialog={showMoreInfoDialog}
shouldBeClickable={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,23 @@ const download = {
timeStart: 123000
}

const erroredFetchLinksdownload = {
errors: null,
loadingMoreFiles: false,
progress: {
percent: 100,
finishedFiles: 10,
totalFiles: 10,
totalTime: 567000
},
downloadId: 'mock-download-id',
state: downloadStates.errorFetchingLinks,
timeStart: 123000
}

const setup = (overrideProps = {}) => {
const deleteAllToastsById = jest.fn()
const clearDownloadHistory = jest.fn()
const copyDownloadPath = jest.fn()
const openDownloadFolder = jest.fn()
const restartDownload = jest.fn()
Expand All @@ -45,6 +60,7 @@ const setup = (overrideProps = {}) => {
<ElectronApiContext.Provider
value={
{
clearDownloadHistory,
copyDownloadPath,
openDownloadFolder,
restartDownload,
Expand All @@ -67,6 +83,7 @@ const setup = (overrideProps = {}) => {

return {
deleteAllToastsById,
clearDownloadHistory,
copyDownloadPath,
openDownloadFolder,
restartDownload
Expand Down Expand Up @@ -158,4 +175,47 @@ describe('DownloadHistoryListItem component', () => {
expect(deleteAllToastsById).toHaveBeenCalledWith('mock-download-id')
})
})

describe('when clicking `Clear Download`', () => {
test('calls clearDownloadHistory', async () => {
const { clearDownloadHistory, deleteAllToastsById } = setup()

const moreActions = screen.getByText('More Actions')
await userEvent.click(moreActions)

const button = screen.getByText('Clear Download')
await userEvent.click(button)

expect(clearDownloadHistory).toHaveBeenCalledTimes(1)
expect(clearDownloadHistory).toHaveBeenCalledWith({ downloadId: 'mock-download-id' })

expect(deleteAllToastsById).toHaveBeenCalledTimes(1)
expect(deleteAllToastsById).toHaveBeenCalledWith('mock-download-id')
})
})

describe('when a `downloadHistoryListItem` is in `errorFetchingLinks` state', () => {
test(' clicking `Clear Download` clearDownloadHistory', async () => {
const { clearDownloadHistory, deleteAllToastsById } = setup(
{ download: erroredFetchLinksdownload }
)
const moreActions = screen.getByText('More Actions')
await userEvent.click(moreActions)

// Ensure the other buttons are not rendering
expect(screen.queryAllByText('Open Folder').length).toEqual(0)
expect(screen.queryAllByText('Copy Folder Path').length).toEqual(0)
expect(screen.queryAllByText('Copy Folder Path').length).toEqual(0)
expect(screen.queryAllByText('Restart Download').length).toEqual(0)

const Clearbutton = screen.getByText('Clear Download')
await userEvent.click(Clearbutton)

expect(clearDownloadHistory).toHaveBeenCalledTimes(1)
expect(clearDownloadHistory).toHaveBeenCalledWith({ downloadId: 'mock-download-id' })

expect(deleteAllToastsById).toHaveBeenCalledTimes(1)
expect(deleteAllToastsById).toHaveBeenCalledWith('mock-download-id')
})
})
})
4 changes: 2 additions & 2 deletions src/main/__tests__/preload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ describe('preload', () => {
test('clearDownloadHistory sends the clearDownloadHistory message', async () => {
await setup()

electronApi.clearDownloadHistory()
electronApi.clearDownloadHistory({ mock: 'data' })

expect(ipcRenderer.send).toHaveBeenCalledTimes(1)
expect(ipcRenderer.send).toHaveBeenCalledWith('clearDownloadHistory')
expect(ipcRenderer.send).toHaveBeenCalledWith('clearDownloadHistory', { mock: 'data' })
})

test('closeWindow sends the closeWindow message', async () => {
Expand Down
40 changes: 40 additions & 0 deletions src/main/eventHandlers/__tests__/clearDownloadHistory.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import clearDownloadHistory from '../clearDownloadHistory'

describe('clearDownloadHistory', () => {
describe('when a downloadId is not passed in', () => {
test('ensure that downloads, files, and pauses which are not active are deleted', async () => {
const info = {}
const database = {
clearDownloadHistoryDownloads: jest.fn()
}

await clearDownloadHistory({
database,
info
})

expect(database.clearDownloadHistoryDownloads).toHaveBeenCalledTimes(1)

expect(database.clearDownloadHistoryDownloads).toHaveBeenCalledWith(undefined)
})
})

describe('when a downloadId is passed in', () => {
test('ensure only the downloads, files, and pauses associated to the downloadId are deleted', async () => {
const info = {
downloadId: 'mock-download-id'
}
const database = {
clearDownloadHistoryDownloads: jest.fn()
}

await clearDownloadHistory({
database,
info
})

expect(database.clearDownloadHistoryDownloads).toHaveBeenCalledTimes(1)
expect(database.clearDownloadHistoryDownloads).toHaveBeenCalledWith('mock-download-id')
})
})
})
19 changes: 19 additions & 0 deletions src/main/eventHandlers/clearDownloadHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @ts-nocheck

/**
* Removes downloads in the download history from the database
* @param {Object} params
* @param {Object} params.database `EddDatabase` instance
* @param {Object} params.info `info` parameter from ipc message
*/
const clearDownloadHistory = async ({
database,
info
}) => {
const { downloadId } = info

// Clear the download(s) in the history
await database.clearDownloadHistoryDownloads(downloadId)
}

export default clearDownloadHistory
2 changes: 1 addition & 1 deletion src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ contextBridge.exposeInMainWorld('electronApi', {
cancelDownloadItem: (data) => ipcRenderer.send('cancelDownloadItem', data),
cancelErroredDownloadItem: (data) => ipcRenderer.send('cancelErroredDownloadItem', data),
clearDownload: (data) => ipcRenderer.send('clearDownload', data),
clearDownloadHistory: () => ipcRenderer.send('clearDownloadHistory'),
clearDownloadHistory: (data) => ipcRenderer.send('clearDownloadHistory', data),
closeWindow: () => ipcRenderer.send('closeWindow'),
copyDownloadPath: (data) => ipcRenderer.send('copyDownloadPath', data),
maximizeWindow: () => ipcRenderer.send('maximizeWindow'),
Expand Down
21 changes: 21 additions & 0 deletions src/main/utils/__tests__/setupEventListeners.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import beginDownload from '../../eventHandlers/beginDownload'
import cancelDownloadItem from '../../eventHandlers/cancelDownloadItem'
import cancelErroredDownloadItem from '../../eventHandlers/cancelErroredDownloadItem'
import clearDownload from '../../eventHandlers/clearDownload'
import clearDownloadHistory from '../../eventHandlers/clearDownloadHistory'
import chooseDownloadLocation from '../../eventHandlers/chooseDownloadLocation'
import copyDownloadPath from '../../eventHandlers/copyDownloadPath'
import didFinishLoad from '../../eventHandlers/didFinishLoad'
Expand Down Expand Up @@ -44,6 +45,7 @@ jest.mock('../../eventHandlers/beginDownload')
jest.mock('../../eventHandlers/cancelDownloadItem')
jest.mock('../../eventHandlers/cancelErroredDownloadItem')
jest.mock('../../eventHandlers/clearDownload')
jest.mock('../../eventHandlers/clearDownloadHistory')
jest.mock('../../eventHandlers/chooseDownloadLocation')
jest.mock('../../eventHandlers/copyDownloadPath')
jest.mock('../../eventHandlers/didFinishLoad')
Expand Down Expand Up @@ -510,6 +512,25 @@ describe('setupEventListeners', () => {
})
})

describe('clearDownloadHistory', () => {
test('calls clearDownloadHistory', () => {
const {
database
} = setup()

const event = {}
const info = { mock: 'info' }

ipcRenderer.send('clearDownloadHistory', event, info)

expect(clearDownloadHistory).toHaveBeenCalledTimes(1)
expect(clearDownloadHistory).toHaveBeenCalledWith({
database,
info
})
})
})

describe('pauseDownloadItem', () => {
test('calls pauseDownloadItem', () => {
const {
Expand Down
44 changes: 44 additions & 0 deletions src/main/utils/database/EddDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,50 @@ class EddDatabase {
return query
}

/**
* Deletes downloads which are on the `downloadHistory` page
* @param {String} downloadId ID of download to create.
*/
async clearDownloadHistoryDownloads(downloadId) {
if (downloadId) {
await this.db('downloads')
.delete()
.where({ id: downloadId })

await this.db('files')
.delete()
.where({ downloadId })

await this.db('pauses')
.delete()
.where({ id: downloadId })

return
}

await this.db('pauses')
.delete()
.whereIn('downloadId', function select() {
this
.select('id')
.from('downloads')
.where({ active: false })
})

await this.db('files')
.delete()
.whereIn('downloadId', function select() {
this
.select('id')
.from('downloads')
.where({ active: false })
})

await this.db('downloads')
.delete()
.where({ 'downloads.active': false })
}

/**
* Creates a new download.
* @param {String} downloadId ID of download to create.
Expand Down
Loading

0 comments on commit 72cb052

Please sign in to comment.