Merge branch 'master' into homeFixes

pull/5370/head
David Disu 3 years ago committed by GitHub
commit fc003d5493
  1. 2
      apps/remix-ide/src/app/editor/examples.js
  2. 2
      apps/remix-ide/src/app/tabs/runTab/model/recorder.js
  3. 7
      apps/remix-ide/src/app/tabs/theme-module.js
  4. 5
      libs/remix-ui/editor/src/lib/remix-ui-editor.css
  5. 4
      libs/remix-ui/helper/src/lib/remix-ui-helper.ts
  6. 2
      libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx
  7. 43
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  8. 4
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  9. 4
      libs/remix-ui/run-tab/src/lib/types/index.ts
  10. 46
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  11. 51
      libs/remix-ui/theme-module/src/lib/remix-ui-theme-module.tsx

@ -313,7 +313,7 @@ const deployWithEthers = `// Right click on the script name and hit "Run" to exe
const readme = `REMIX EXAMPLE PROJECT
Remix example project is present when Remix loads very first time or there are no files existing in the File Explorer.
Remix example project is present when Remix loads for the very first time or there are no files existing in the File Explorer.
It contains 3 directories:
1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name.

@ -259,7 +259,7 @@ class Recorder {
cb(err)
}
)
}, () => { self.setListen(true); self.clearAll() })
}, () => { self.setListen(true) })
}
runScenario (json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) {

@ -35,15 +35,17 @@ export class ThemeModule extends Plugin {
}
this.themes = themes.reduce((acc, theme) => {
theme.url = window.location.origin + window.location.pathname + theme.url
return { ...acc, [theme.name]: theme }
return { ...acc, [theme.name.toLocaleLowerCase()]: theme }
}, {})
this._paq = _paq
let queryTheme = (new QueryParams()).get().theme
queryTheme = queryTheme && queryTheme.toLocaleLowerCase()
queryTheme = this.themes[queryTheme] ? queryTheme : null
let currentTheme = this._deps.config.get('settings/theme')
currentTheme = currentTheme && currentTheme.toLocaleLowerCase()
currentTheme = this.themes[currentTheme] ? currentTheme : null
this.currentThemeState = { queryTheme, currentTheme }
this.active = queryTheme || currentTheme || 'Dark'
this.active = queryTheme || currentTheme || 'dark'
this.forced = !!queryTheme
}
@ -82,6 +84,7 @@ export class ThemeModule extends Plugin {
* @param {string} [themeName] - The name of the theme
*/
switchTheme (themeName) {
themeName = themeName && themeName.toLocaleLowerCase()
if (themeName && !Object.keys(this.themes).includes(themeName)) {
throw new Error(`Theme ${themeName} doesn't exist`)
}

@ -10,6 +10,11 @@
width: auto;
}
.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) {
max-width: none !important;
word-wrap: break-word;
}
.contextview {
opacity: 1;
position: absolute;

@ -37,9 +37,9 @@ export const createNonClashingNameAsync = async (name: string, fileManager, pref
let exist = true
do {
const isDuplicate = await fileManager.exists(name + _counter + prefix + '.' + ext)
const isDuplicate = await fileManager.exists(name + (_counter || '') + prefix + '.' + ext)
if (isDuplicate) _counter = (_counter | 0) + 1
if (isDuplicate) _counter = (_counter || 0) + 1
else exist = false
} while (exist)
const counter = _counter || ''

@ -56,7 +56,7 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
<article className='remember'>
<div className='form-check'>
{rememberSwitch()}
<label className="form-check-label" data-id="permissionHandlerRememberChoice">Remember this choice</label>
<label htmlFor='remember' className="form-check-label" data-id="permissionHandlerRememberChoice">Remember this choice</label>
</div>
<button className="btn btn-sm" onClick={reset}>Reset all Permissions</button>
</article>

@ -2,7 +2,7 @@
import React from 'react'
import * as ethJSUtil from 'ethereumjs-util'
import Web3 from 'web3'
import { addressToString, createNonClashingNameAsync, shortenAddress } from '@remix-ui/helper'
import { addressToString, createNonClashingNameAsync, extractNameFromKey, shortenAddress } from '@remix-ui/helper'
import { addNewInstance, addProvider, clearAllInstances, clearRecorderCount, displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, fetchContractListSuccess, hidePopUp, removeExistingInstance, removeProvider, resetUdapp, setBaseFeePerGas, setConfirmSettings, setCurrentFile, setDecodedResponse, setEnvToasterContent, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setLoadType, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setRecorderCount, setSelectedAccount, setSendUnit, setSendValue, setTxFeeContent, setWeb3Dialog } from './payload'
import { RunTab } from '../types/run-tab'
import { CompilerAbstract } from '@remix-project/remix-solidity'
@ -622,38 +622,39 @@ export const runTransactions = (
)
}
const saveScenario = (promptCb, cb) => {
const saveScenario = (newPath: string, provider, promptCb, cb) => {
const txJSON = JSON.stringify(plugin.recorder.getAll(), null, 2)
const path = plugin.fileManager.currentPath()
promptCb(path, async () => {
const fileProvider = plugin.fileManager.fileProviderOf(path)
if (!fileProvider) return
const newFile = path + '/' + plugin.REACT_API.recorder.pathToScenario
promptCb(() => {
try {
const newPath = await createNonClashingNameAsync(newFile, plugin.fileManager)
if (!fileProvider.set(newPath, txJSON)) return cb('Failed to create file ' + newFile)
plugin.fileManager.open(newFile)
if (!provider.set(newPath, txJSON)) return cb('Failed to create file ' + newPath)
plugin.fileManager.open(newPath)
} catch (error) {
if (error) return cb('Failed to create file. ' + newFile + ' ' + error)
if (error) return cb('Failed to create file. ' + newPath + ' ' + error)
}
})
}
export const storeScenario = (prompt: (msg: string) => JSX.Element) => {
saveScenario(
(path, cb) => {
dispatch(displayNotification('Save transactions as scenario', prompt('Transactions will be saved in a file under ' + path), 'Ok', 'Cancel', cb, null))
export const storeScenario = async (prompt: (msg: string, defaultValue: string) => JSX.Element) => {
const path = plugin.fileManager.currentPath()
const fileProvider = plugin.fileManager.fileProviderOf(path)
if (!fileProvider) return displayNotification('Alert', 'Invalid File Provider', 'OK', null)
const newPath = await createNonClashingNameAsync(path + '/' + plugin.REACT_API.recorder.pathToScenario, plugin.fileManager)
const newName = extractNameFromKey(newPath)
saveScenario(newPath, fileProvider,
(cb) => {
dispatch(displayNotification('Save transactions as scenario', prompt('Transactions will be saved in a file under ' + path, newName), 'OK', 'Cancel', cb, null))
},
(error) => {
if (error) return dispatch(displayNotification('Alert', error, 'Ok', null))
if (error) return dispatch(displayNotification('Alert', error, 'OK', null))
}
)
}
const runScenario = (file: string, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => {
if (!file) return dispatch(displayNotification('Alert', 'Unable to run scenerio, no specified scenario file', 'Ok', null))
if (!file) return dispatch(displayNotification('Alert', 'Unable to run scenerio, no specified scenario file', 'OK', null))
plugin.fileManager.readFile(file).then((json) => {
// TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily
@ -664,7 +665,7 @@ const runScenario = (file: string, gasEstimationPrompt: (msg: string) => JSX.Ele
}, (okCb, cancelCb) => {
promptHandler(passphrasePrompt, okCb, cancelCb)
}, (msg) => {
dispatch(displayNotification('Alert', msg, 'Ok', null))
dispatch(displayNotification('Alert', msg, 'OK', null))
}, (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
confirmationHandler(confirmDialogContent, network, tx, gasEstimation, continueTxExecution, cancelCb)
}, (msg: string) => {
@ -673,11 +674,11 @@ const runScenario = (file: string, gasEstimationPrompt: (msg: string) => JSX.Ele
return terminalLogger(log)
}, (error, abi, address, contractName) => {
if (error) {
return dispatch(displayNotification('Alert', error, 'Ok', null))
return dispatch(displayNotification('Alert', error, 'OK', null))
}
addInstance({ name: contractName, address, abi })
})
}).catch((error) => dispatch(displayNotification('Alert', error, 'Ok', null)))
}).catch((error) => dispatch(displayNotification('Alert', error, 'OK', null)))
}
export const runCurrentScenario = (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => {

@ -172,8 +172,8 @@ export function RunTabUI (props: RunTabProps) {
return <PassphrasePrompt message={message} setPassphrase={setPassphrasePrompt} defaultValue={runTab.passphrase} />
}
const scenarioPrompt = (message: string) => {
return <ScenarioPrompt message={message} setScenarioPath={updateScenarioPath} defaultValue={runTab.recorder.pathToScenario} />
const scenarioPrompt = (message: string, defaultValue) => {
return <ScenarioPrompt message={message} setScenarioPath={updateScenarioPath} defaultValue={defaultValue} />
}
const mainnetPrompt = (tx: Tx, network: Network, amount: string, gasEstimation: string, gasFees: (maxFee: string, cb: (txFeeText: string, priceStatus: boolean) => void) => void, determineGasPrice: (cb: (txFeeText: string, gasPriceValue: string, gasPriceStatus: boolean) => void) => void) => {

@ -189,13 +189,13 @@ export interface ContractDropdownProps {
}
export interface RecorderProps {
storeScenario: (prompt: (msg: string) => JSX.Element) => void,
storeScenario: (prompt: (msg: string, defaultValue: string) => JSX.Element) => void,
runCurrentScenario: (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => void,
logBuilder: (msg: string) => JSX.Element,
mainnetPrompt: MainnetPrompt,
gasEstimationPrompt: (msg: string) => JSX.Element,
passphrasePrompt: (msg: string) => JSX.Element,
scenarioPrompt: (msg: string) => JSX.Element,
scenarioPrompt: (msg: string, defaultValue: string) => JSX.Element,
count: number
}

@ -67,6 +67,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
let [readyTestsNumber, setReadyTestsNumber] = useState<number>(0) // eslint-disable-line
let [runningTestsNumber, setRunningTestsNumber] = useState<number>(0) // eslint-disable-line
const areTestsRunning = useRef<boolean>(false)
const hasBeenStopped = useRef<boolean>(false)
const isDebugging = useRef<boolean>(false)
const allTests = useRef<string[]>([])
@ -74,7 +75,6 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const currentErrors:any = useRef([]) // eslint-disable-line @typescript-eslint/no-explicit-any
const defaultPath = 'tests'
let areTestsRunning = false
let runningTestFileName: string
const filesContent: Record<string, Record<string, string>> = {}
@ -113,7 +113,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
allTests.current = tests
selectedTests.current = [...allTests.current]
updateTestFileList()
if (!areTestsRunning) await updateRunAction(file)
if (!areTestsRunning.current) await updateRunAction(file)
} catch (e: any) { // eslint-disable-line @typescript-eslint/no-explicit-any
console.log(e)
setToasterMsg(e)
@ -153,7 +153,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
await setCurrentPath(defaultPath)
})
testTab.fileManager.events.on('noFileSelected', () => { }) // eslint-disable-line
testTab.fileManager.events.on('noFileSelected', async () => { await updateForNewCurrent() })
testTab.fileManager.events.on('currentFileChanged', async (file: string) => await updateForNewCurrent(file))
}, []) // eslint-disable-line
@ -167,15 +167,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const handleTestDirInput = async (e: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
let testDirInput = trimTestDirInput(e.target.value)
testDirInput = helper.removeMultipleSlashes(testDirInput)
if (testDirInput !== '/') testDirInput = helper.removeTrailingSlashes(testDirInput)
setInputPathValue(testDirInput)
if (e.key === 'Enter') {
if (await testTabLogic.pathExists(testDirInput)) {
testTabLogic.setCurrentPath(testDirInput)
await updateForNewCurrent()
return
}
}
if (testDirInput) {
if (testDirInput.endsWith('/') && testDirInput !== '/') {
testDirInput = helper.removeTrailingSlashes(testDirInput)
@ -205,17 +197,6 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
}
}
const handleEnter = async (e: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
let inputPath = e.target.value
inputPath = helper.removeMultipleSlashes(trimTestDirInput(inputPath))
setInputPathValue(inputPath)
if (disableCreateButton) {
if (await testTabLogic.pathExists(inputPath)) {
await setCurrentPath(inputPath)
}
}
}
const handleCreateFolder = async () => {
let inputPath = trimTestDirInput(inputPathValue)
let path = helper.removeMultipleSlashes(inputPath)
@ -519,7 +500,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
if (selectedTests.current?.length !== 0) {
setDisableRunButton(false)
}
areTestsRunning = false
areTestsRunning.current = false
}
}
@ -566,7 +547,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
}
const runTests = () => {
areTestsRunning = true
areTestsRunning.current = true
hasBeenStopped.current = false
readyTestsNumber = 0
setReadyTestsNumber(readyTestsNumber)
@ -587,14 +568,14 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const updateRunAction = async (currentFile: any = null) => { // eslint-disable-line @typescript-eslint/no-explicit-any
const isSolidityActive = await testTab.appManager.isActive('solidity')
if (!isSolidityActive || !selectedTests.current?.length) {
// setDisableRunButton(true)
if (!isSolidityActive || !selectedTests.current.length) {
setDisableRunButton(true)
if (!currentFile || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) {
setRunButtonTitle('No solidity file selected')
} else {
setRunButtonTitle('The "Solidity Plugin" should be activated')
}
}
} else setDisableRunButton(false)
}
const stopTests = () => {
@ -611,7 +592,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const toggleCheckbox = (eChecked: boolean, index: number) => {
testFiles[index].checked = eChecked
setTestFiles(testFiles)
setTestFiles([...testFiles])
selectedTests.current = getCurrentSelectedTests()
if (eChecked) {
setCheckSelectAll(true)
@ -628,7 +609,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const checkAll = (event: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
testFiles.forEach((testFileObj) => testFileObj.checked = event.target.checked)
setTestFiles(testFiles)
setTestFiles([...testFiles])
setCheckSelectAll(event.target.checked)
if (event.target.checked) {
selectedTests.current = getCurrentSelectedTests()
@ -646,7 +627,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
}
else
testFiles = []
setTestFiles(testFiles)
setTestFiles([...testFiles])
}
return (
@ -674,8 +655,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
title="Press 'Enter' to change the path for test files."
style={{ backgroundImage: "var(--primary)" }}
onKeyDown={() => { if (inputPathValue === '/') setInputPathValue('')} }
onKeyUp={handleTestDirInput}
onChange={handleEnter}
onChange={handleTestDirInput}
onClick = {() => { if (inputPathValue === '/') setInputPathValue('')} }
/>
<button
@ -728,7 +708,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
/>
<label className="text-nowrap pl-2 mb-0" htmlFor="checkAllTests"> Select all </label>
</div>
<div className="testList py-2 mt-0 border-bottom">{testFiles?.length ? testFiles.map((testFileObj: TestObject, index) => {
<div className="testList py-2 mt-0 border-bottom">{testFiles.length ? testFiles.map((testFileObj: TestObject, index) => {
const elemId = `singleTest${testFileObj.fileName}`
return (
<div className="d-flex align-items-center py-1" key={index}>

@ -8,55 +8,6 @@ export interface RemixUiThemeModuleProps {
themeModule: ThemeModule;
}
const defaultThemes = [
{
name: 'Dark',
quality: 'dark',
url: 'assets/css/themes/remix-dark_tvx1s2.css'
},
{
name: 'Light',
quality: 'light',
url: 'assets/css/themes/remix-light_powaqg.css'
},
{
name: 'Midcentury',
quality: 'light',
url: 'assets/css/themes/remix-midcentury_hrzph3.css'
},
{
name: 'Black',
quality: 'dark',
url: 'assets/css/themes/remix-black_undtds.css'
},
{
name: 'Candy',
quality: 'light',
url: 'assets/css/themes/remix-candy_ikhg4m.css'
},
{
name: 'Cerulean',
quality: 'light',
url: 'assets/css/themes/bootstrap-cerulean.min.css'
},
{
name: 'Flatly',
quality: 'light',
url: 'assets/css/themes/bootstrap-flatly.min.css'
},
{
name: 'Spacelab',
quality: 'light',
url: 'assets/css/themes/bootstrap-spacelab.min.css'
},
{
name: 'Cyborg',
quality: 'dark',
url: 'assets/css/themes/bootstrap-cyborg.min.css'
}
];
export function RemixUiThemeModule({ themeModule }: RemixUiThemeModuleProps) {
const [themeName, setThemeName] = useState('')
@ -85,7 +36,7 @@ export function RemixUiThemeModule({ themeModule }: RemixUiThemeModuleProps) {
name="theme"
id={theme.name}
data-id={`settingsTabTheme${theme.name}`}
checked={themeModule.active === theme.name}
checked={themeModule.active === theme.name.toLocaleLowerCase()}
/>
<label
className="form-check-label custom-control-label"

Loading…
Cancel
Save