Add UI tests for clipboard copy and file download in Electron and browser modes (#1004)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thomasnordquist <7721625+thomasnordquist@users.noreply.github.com>
This commit is contained in:
@@ -15,7 +15,7 @@ interface Props {
|
||||
*/
|
||||
function ClearAdornment(props: Props) {
|
||||
const theme = useTheme()
|
||||
|
||||
|
||||
if (!props.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@ import FileCopy from '@mui/icons-material/FileCopy'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import { connect } from 'react-redux'
|
||||
import { globalActions } from '../../actions'
|
||||
|
||||
// Fallback for older browsers or when clipboard API is not available
|
||||
const copyTextFallback = require('copy-text-to-clipboard')
|
||||
import copyTextFallback from 'copy-text-to-clipboard'
|
||||
|
||||
async function copyToClipboard(text: string): Promise<boolean> {
|
||||
try {
|
||||
@@ -19,7 +17,7 @@ async function copyToClipboard(text: string): Promise<boolean> {
|
||||
} catch (error) {
|
||||
console.warn('Clipboard API failed, using fallback:', error)
|
||||
}
|
||||
|
||||
|
||||
// Fallback to copy-text-to-clipboard library
|
||||
return copyTextFallback(text)
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ class CustomIconButton extends React.PureComponent<Props, {}> {
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<IconButton
|
||||
className={this.props.classes.button}
|
||||
style={this.props.style}
|
||||
<IconButton
|
||||
className={this.props.classes.button}
|
||||
style={this.props.style}
|
||||
onClick={this.onClick}
|
||||
data-testid={this.props['data-testid']}
|
||||
>
|
||||
|
||||
@@ -7,14 +7,56 @@ import { SaveAlt } from '@mui/icons-material'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import { rendererRpc, writeToFile } from '../../eventBus'
|
||||
import { makeSaveDialogRpc } from '../../../../events/OpenDialogRequest'
|
||||
import { isBrowserMode } from '../../utils/browserMode'
|
||||
|
||||
import { globalActions } from '../../actions'
|
||||
|
||||
/**
|
||||
* Download a file in browser mode using blob URL
|
||||
* @param data Base64-encoded file data
|
||||
* @param filename Filename for the download
|
||||
* @returns The filename that was downloaded
|
||||
*/
|
||||
function downloadFileInBrowser(data: string, filename: string): string {
|
||||
// Decode base64 data
|
||||
const binaryString = atob(data)
|
||||
const bytes = new Uint8Array(binaryString.length)
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i)
|
||||
}
|
||||
|
||||
// Create blob and download
|
||||
const blob = new Blob([bytes], { type: 'application/octet-stream' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = filename
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
|
||||
return filename
|
||||
}
|
||||
|
||||
export async function saveToFile(data: string): Promise<string | undefined> {
|
||||
const rejectReasons = {
|
||||
errorWritingFile: 'Error writing file',
|
||||
}
|
||||
|
||||
// In browser mode, use browser download
|
||||
if (isBrowserMode) {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
||||
const filename = `mqtt-message-${timestamp}.bin`
|
||||
try {
|
||||
downloadFileInBrowser(data, filename)
|
||||
return filename
|
||||
} catch (error) {
|
||||
throw rejectReasons.errorWritingFile
|
||||
}
|
||||
}
|
||||
|
||||
// In Electron mode, use native file dialog
|
||||
const { canceled, filePath } = await rendererRpc.call(makeSaveDialogRpc(), {
|
||||
securityScopedBookmarks: true,
|
||||
})
|
||||
@@ -67,7 +109,7 @@ class Save extends React.PureComponent<Props, State> {
|
||||
)
|
||||
|
||||
return (
|
||||
<CustomIconButton onClick={this.handleClick} tooltip="Save to file">
|
||||
<CustomIconButton onClick={this.handleClick} tooltip="Save to file" data-testid="save-button">
|
||||
<div style={{ marginTop: '2px' }}>{icon}</div>
|
||||
</CustomIconButton>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user