Merge branch 'master' into intl

pull/5370/head
drafish 2 years ago
commit 7a85086944
  1. 2
      apps/remix-ide-e2e/src/commands/clickElementAtPosition.ts
  2. 4
      apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts
  3. 67
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  4. 46
      libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx
  5. 13
      libs/remix-ui/panel/src/lib/plugins/panel-header.tsx
  6. 66
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  7. 59
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  8. 158
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  9. 18
      libs/remix-ui/static-analyser/src/lib/Button/StaticAnalyserButton.tsx
  10. 6
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx
  11. 115
      libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx
  12. 3
      libs/remix-ui/workspace/src/lib/types/index.ts

@ -14,7 +14,7 @@ class ClickElement extends EventEmitter {
}
function _clickElement (browser: NightwatchBrowser, cssSelector: string, index: number, forceSelectIfUnselected: boolean, cb: VoidFunction) {
browser.waitForElementPresent(cssSelector)
browser.waitForElementPresent(cssSelector, 5000)
.execute(function (cssSelector: string, index: number, forceSelectIfUnselected: boolean) {
const elem = document.querySelectorAll(cssSelector)[index] as HTMLElement
if (forceSelectIfUnselected) {

@ -216,7 +216,7 @@ module.exports = {
.waitForElementVisible(autoCompleteLineElement('importedbook'))
.waitForElementVisible(autoCompleteLineElement('importpublicstring'))
.waitForElementVisible(autoCompleteLineElement('publicimport'))
// no private
// no private
.waitForElementNotPresent(autoCompleteLineElement('importprivatestring'))
.waitForElementNotPresent(autoCompleteLineElement('privateimport'))
// no internal
@ -523,4 +523,4 @@ module.exports = {
.sendKeys(this.Keys.ENTER)
})
}
}
}

@ -152,9 +152,12 @@ module.exports = {
.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.addFile('myTests/simple_storage_test.sol', sources[0]['tests/simple_storage_test.sol'])
.clickLaunchIcon('solidityUnitTesting')
.clearValue('*[data-id="uiPathInput"]')
.setValue('*[data-id="uiPathInput"]', 'myTests')
.execute(() => {
const myQuery: any = document.getElementById('utPath')
myQuery.value = 'myTests'
})
.click('*[data-id="testTabGenerateTestFolder"]')
.saveScreenshot('./reports/screenshots/changeCurrentPathg3.png')
.clickElementAtPosition('.singleTest', 0, { forceSelectIfUnselected: true })
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 60000)
@ -359,15 +362,15 @@ const sources = [
contract SimpleStorage {
uint public storedData;
constructor() {
storedData = 100;
}
function set(uint x) public {
storedData = x;
}
function get() public view returns (uint retVal) {
return storedData;
}
@ -408,7 +411,7 @@ const sources = [
pragma solidity >=0.4.22 <0.9.0;
contract Kickstarter {
enum State { Started, Completed }
struct Project {
address owner;
string name;
@ -420,10 +423,10 @@ const sources = [
}
uint numProjects;
Project[] public projects;
constructor() {
}
function createProject(string memory name, uint goal) public {
projects.push(); // new line
Project storage project = projects[projects.length - 1];
@ -432,28 +435,28 @@ const sources = [
project.owner = msg.sender;
project.state = State.Started;
}
function fundProject(uint projectId) payable public {
Project storage project = projects[projectId];
// require project exists
// PLEASE CHECK / or erase
// not this: require(projects[projectId].exists, "the project must exist to be funded");
// require for... underflow/overflow protection
project.funders[msg.sender] += msg.value;
project.amountContributed += msg.value;
project.fundsAvailable += msg.value;
if (project.amountContributed >= project.goal) {
project.state = State.Completed;
}
}
// this function is here because we can't use web3 when using the VM
function getContractBalance() public view returns(uint balance) {
return address(this).balance;
}
}
`
},
@ -470,13 +473,13 @@ const sources = [
enum State { Started, Completed }
Kickstarter kickstarter;
function beforeAll () public {
kickstarter = new Kickstarter();
kickstarter.createProject("ProjectA", 123000);
kickstarter.createProject("ProjectB", 100);
}
/// #sender: account-1
/// #value: 10000000
function checkProjectExists () public payable {
@ -512,14 +515,14 @@ const sources = [
(address owner, string memory name, uint goal, uint fundsAvailable, uint amountContributed, Kickstarter.State state) = kickstarter.projects(0);
Assert.equal(amountContributed, 120000, "contributed amount is incorrect");
}
}
`
},
'compilationError_test.sol': {
content: `
pragma solidity ^0.8.0;
contract failOnCompilation {
fallback() {
@ -547,7 +550,7 @@ const sources = [
uint c = a+b;
Assert.equal(a+b, c, "wrong value");
}
}
}
`
},
'tests/ballotFailedDebug_test.sol': {
@ -556,31 +559,31 @@ const sources = [
pragma solidity >=0.7.0 <0.9.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "../contracts/3_Ballot.sol";
contract BallotTest {
bytes32[] proposalNames;
Ballot ballotToTest;
function beforeAll () public {
proposalNames.push(bytes32("candidate1"));
ballotToTest = new Ballot(proposalNames);
}
function checkWinningProposalFailed () public {
ballotToTest.vote(1);
Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
}
function checkWinningProposalPassed () public {
ballotToTest.vote(0);
Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
}
function checkWinningProposalAgain () public {
Assert.equal(ballotToTest.winningProposal(), uint(1), "proposal at index 0 should be the winning proposal");
}
function checkWinninProposalWithReturnValue () public view returns (bool) {
return ballotToTest.winningProposal() == 0;
}
@ -594,17 +597,17 @@ const sources = [
import "../contracts/3_Ballot.sol";
import "hardhat/console.sol";
contract BallotTest {
bytes32[] proposalNames;
Ballot ballotToTest;
function beforeAll () public {
proposalNames.push(bytes32("candidate1"));
ballotToTest = new Ballot(proposalNames);
}
function checkWinningProposal () public {
console.log("Inside checkWinningProposal");
ballotToTest.vote(1); // This will revert the transaction
@ -617,13 +620,13 @@ const sources = [
pragma solidity >=0.7.0 <0.9.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "hardhat/console.sol";
contract hhLogs {
function beforeAll () public {
console.log('Inside beforeAll');
}
function checkSender () public {
console.log('msg.sender is %s', msg.sender);
Assert.ok(true, "should be true");

@ -1,5 +1,8 @@
import React, { CSSProperties } from 'react' //eslint-disable-line
import { OverlayProps, OverlayTrigger, OverlayTriggerProps, Tooltip } from 'react-bootstrap'// eslint-disable-line
import './remix-ui-checkbox.css'
type Placement = import('react-overlays/usePopper').Placement;
/* eslint-disable-next-line */
export interface RemixUiCheckboxProps {
@ -15,6 +18,7 @@ export interface RemixUiCheckboxProps {
title?: string
visibility?: string
display?: string
tooltipPlacement?: Placement
}
export const RemixUiCheckbox = ({
@ -29,24 +33,34 @@ export const RemixUiCheckbox = ({
categoryId,
title,
visibility,
display = 'flex'
display = 'flex',
tooltipPlacement = 'right-start'
}: RemixUiCheckboxProps) => {
return (
<div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" title={title} style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}>
<input
id={id}
type={inputType}
onChange={onChange}
style={{ verticalAlign: 'bottom' }}
name={name}
className="custom-control-input"
checked={checked}
/>
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{ paddingTop: '0.15rem' }}>
{name ? <div className="font-weight-bold">{itemName}</div> : ''}
{label}
</label>
</div>
<OverlayTrigger
placement={tooltipPlacement}
overlay={
<Tooltip id={`${name}Tooltip`}>
<span>{title}</span>
</Tooltip>
}
>
<div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}>
<input
id={id}
type={inputType}
onChange={onChange}
style={{ verticalAlign: 'bottom' }}
name={name}
className="custom-control-input"
checked={checked}
/>
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{ paddingTop: '0.15rem' }}>
{name ? <div className="font-weight-bold">{itemName}</div> : ''}
{label}
</label>
</div>
</OverlayTrigger>
)
}

@ -3,6 +3,7 @@ import React, { useEffect, useRef, useState } from 'react' // eslint-disable-lin
import { FormattedMessage } from 'react-intl'
import { PluginRecord } from '../types'
import './panel.css'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
export interface RemixPanelProps {
plugins: Record<string, PluginRecord>;
@ -35,9 +36,15 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
<div className="d-flex flex-row">
{plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && (<i aria-hidden="true" className="text-success mt-1 px-1 fas fa-check" title="Maintained by Remix"></i>)}
</div>
<div className="swapitHeaderInfoSection d-flex justify-content-between" data-id='swapitHeaderInfoSectionId' onClick={toggleClass} title="Plugin info">
<i className={`px-2 ml-2 pt-1 pb-4 ${!toggleExpander ? 'fas fa-angle-right' : 'fas fa-angle-down bg-light'}`} aria-hidden="true"></i>
</div>
<OverlayTrigger overlay={
<Tooltip className="text-nowrap" id="pluginInfoTooltip">
<span>Plugin info</span>
</Tooltip>
} placement={'right-end'}>
<div className="swapitHeaderInfoSection d-flex justify-content-between" data-id='swapitHeaderInfoSectionId' onClick={toggleClass}>
<i className={`px-2 ml-2 pt-1 pb-4 ${!toggleExpander ? 'fas fa-angle-right' : 'fas fa-angle-down bg-light'}`} aria-hidden="true"></i>
</div>
</OverlayTrigger>
</div>
</div>
<div className={`bg-light mx-3 mb-2 p-3 pt-1 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>

@ -742,15 +742,26 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<label className="remixui_compilerLabel form-check-label" htmlFor="versionSelector">
<FormattedMessage id='solidity.compiler' defaultMessage='Compiler' />
</label>
<span
className="far fa-plus border-0 p-0 ml-3"
onClick={() => promptCompiler()}
title={intl.formatMessage({
id: 'solidity.addACustomCompilerWithURL',
defaultMessage: "Add a custom compiler with URL",
})}
></span>
<span className="fa fa-file-text-o border-0 p-0 ml-2" onClick={() => showCompilerLicense()} title="See compiler license"></span>
<OverlayTrigger
placement="top"
overlay={
<Tooltip id="promptCompilerTooltip" className="text-nowrap">
<span><FormattedMessage id='solidity.addACustomCompilerWithURL' defaultMessage='Add a custom compiler with URL' /></span>
</Tooltip>
}
>
<span className="far fa-plus border-0 p-0 ml-3" onClick={() => promptCompiler()}></span>
</OverlayTrigger>
<OverlayTrigger
placement="top"
overlay={
<Tooltip id="showCompilerTooltip" className="text-nowrap">
<span>{"See compiler license"}</span>
</Tooltip>
}
>
<span className="fa fa-file-text-o border-0 p-0 ml-2" onClick={() => showCompilerLicense()}></span>
</OverlayTrigger>
<select value={state.selectedVersion || state.defaultVersion} onChange={(e) => handleLoadVersion(e.target.value)} className="custom-select" id="versionSelector" disabled={state.allversions.length <= 0}>
{state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === state.defaultVersion ? 'selected' : ''}>{state.defaultVersion}</option>}
{state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === 'builtin' ? 'selected' : ''}>builtin</option>}
@ -846,10 +857,19 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">
<FormattedMessage id='solidity.language' defaultMessage='Language' />
</label>
<select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Language specification available from Compiler >= v0.5.7">
<option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
<option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
</select>
<OverlayTrigger
placement="right-start"
overlay={
<Tooltip id="compilerLabelTooltip" className="text-nowrap">
<span>{'Language specification available from Compiler >= v0.5.7'}</span>
</Tooltip>
}
>
<select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector">
<option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
<option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
</select>
</OverlayTrigger>
</div>
<div className="mb-2 ml-4">
<label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector">
@ -886,11 +906,21 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</label>
</div>
<div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}>
{(!showFilePathInput && state.useFileConfiguration) && <span
title="Click to open the config file"
onClick={configFilePath === '' ? () => { } : async () => { await openFile() }}
className="py-2 remixui_compilerConfigPath"
>{configFilePath === '' ? 'No file selected.' : configFilePath}</span>}
{(!showFilePathInput && state.useFileConfiguration) && <OverlayTrigger
placement="bottom"
overlay={
<Tooltip id="configfileTooltip" className="text-nowrap">
<span>
Click to open the config file
</span>
</Tooltip>
}
>
<span
onClick={configFilePath === '' ? () => { } : async () => { await openFile() }}
className="py-2 remixui_compilerConfigPath"
>{configFilePath === '' ? 'No file selected.' : configFilePath}</span>
</OverlayTrigger>}
{(!showFilePathInput && !state.useFileConfiguration) && <span className="py-2 text-secondary">{configFilePath}</span>}
<input
ref={configFilePathInput}

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react' // eslint-disable-line
import React, { useState, useEffect, Fragment } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import { ContractSelectionProps } from './types'
import { PublishToStorage } from '@remix-ui/publish-to-storage' // eslint-disable-line
@ -6,6 +6,7 @@ import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-l
import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import './css/style.css'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
export const ContractSelection = (props: ContractSelectionProps) => {
const { api, compiledFileName, contractsDetails, contractList, modal } = props
@ -198,16 +199,56 @@ export const ContractSelection = (props: ContractSelectionProps) => {
</select>
</div>
<article className="mt-2 pb-0">
<button id="publishOnIpfs" className="btn btn-secondary btn-block" title="Publish on Ipfs" onClick={() => { handlePublishToStorage('ipfs') }}>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Ipfs</span>
<img id="ipfsLogo" className="remixui_storageLogo ml-2" src="assets/img/ipfs.webp" />
<button id="publishOnIpfs" className="btn btn-secondary btn-block" onClick={() => { handlePublishToStorage('ipfs') }}>
<OverlayTrigger
placement="right-start"
overlay={
<Tooltip
id="publishOnIpfsTooltip"
className="text-nowrap"
>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Ipfs</span>
</Tooltip>
}
>
<span>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Ipfs</span>
<img id="ipfsLogo" className="remixui_storageLogo ml-2" src="assets/img/ipfs.webp" />
</span>
</OverlayTrigger>
</button>
<button id="publishOnSwarm" className="btn btn-secondary btn-block" title="Publish on Swarm" onClick={() => { handlePublishToStorage('swarm') }}>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Swarm</span>
<img id="swarmLogo" className="remixui_storageLogo ml-2" src="assets/img/swarm.webp" />
<button id="publishOnSwarm" className="btn btn-secondary btn-block" onClick={() => { handlePublishToStorage('swarm') }}>
<OverlayTrigger
placement="right-start"
overlay={
<Tooltip
id="publishOnSwarmTooltip"
className="text-nowrap"
>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Swarm</span>
</Tooltip>
}
>
<span>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Swarm</span>
<img id="swarmLogo" className="remixui_storageLogo ml-2" src="assets/img/swarm.webp" />
</span>
</OverlayTrigger>
</button>
<button data-id="compilation-details" className="btn btn-secondary btn-block" title="Display Contract Details" onClick={() => { details() }}>
<FormattedMessage id='solidity.compilationDetails' defaultMessage='Compilation Details' />
<button data-id="compilation-details" className="btn btn-secondary btn-block" onClick={() => { details() }}>
<OverlayTrigger
placement="right-start"
overlay={
<Tooltip
id="CompilationDetailsTooltip"
className="text-nowrap"
>
<span>Display Contract Details</span>
</Tooltip>
}
>
<span><FormattedMessage id='solidity.compilationDetails' defaultMessage='Compilation Details' /></span>
</OverlayTrigger>
</button>
{/* Copy to Clipboard */}
<div className="remixui_contractHelperButtons">

@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect, ReactElement } from 'react' // esli
import * as semver from 'semver'
import { eachOfSeries } from 'async' // eslint-disable-line
import type Web3 from 'web3'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { canUseWorker, urlFromVersion } from '@remix-project/remix-solidity'
import { Renderer } from '@remix-ui/renderer' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
@ -198,7 +199,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
if (await testTabLogic.pathExists(testDirInput)) {
setDisableCreateButton(true)
setDisableGenerateButton(false)
} else {
// Enable Create button
setDisableCreateButton(false)
@ -296,20 +297,26 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
let label
if (index > -1) {
const className = "alert-danger d-inline-block mb-1 mr-1 p-1 failed_" + runningTestFileName
label = (<div
label = (<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>At least one contract test failed</span>
</Tooltip>
}><div
className={className}
title="At least one contract test failed"
>
FAIL
</div>)
</div></OverlayTrigger>)
} else {
const className = "alert-success d-inline-block mb-1 mr-1 p-1 passed_" + runningTestFileName
label = (<div
label = (<OverlayTrigger placement={'top-end'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>All contract tests passed</span>
</Tooltip>
}><div
className={className}
title="All contract tests passed"
>
PASS
</div>)
</div></OverlayTrigger>)
}
// show contract and file name with label
const ContractCard: ReactElement = (
@ -335,8 +342,12 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
if (test.debugTxHash) {
const { web3, debugTxHash } = test
debugBtn = (
<div id={test.value.replaceAll(' ', '_')} className="btn border btn btn-sm ml-1" style={{ cursor: 'pointer' }} title="Start debugging" onClick={() => startDebug(debugTxHash, web3)}>
<i className="fas fa-bug"></i>
<div id={test.value.replaceAll(' ', '_')} className="btn border btn btn-sm ml-1" style={{ cursor: 'pointer' }} onClick={() => startDebug(debugTxHash, web3)}>
<OverlayTrigger placement={'top-start'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>Start debugging</span>
</Tooltip>
}><i className="fas fa-bug"></i></OverlayTrigger>
</div>
)
}
@ -663,57 +674,102 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
})
}
</datalist>
<input
list="utPathList"
className="inputFolder custom-select"
id="utPath"
data-id="uiPathInput"
name="utPath"
value={inputPathValue}
title="Press 'Enter' to change the path for test files."
style={{ backgroundImage: "var(--primary)" }}
onKeyDown={() => { if (inputPathValue === '/') setInputPathValue('')} }
onChange={handleTestDirInput}
onClick = {() => { if (inputPathValue === '/') setInputPathValue('')} }
/>
<button
className="btn border ml-2"
data-id="testTabGenerateTestFolder"
title="Create a test folder"
disabled={disableCreateButton}
onClick={handleCreateFolder}
<OverlayTrigger
placement="top-end"
overlay={
<Tooltip className="text-nowrap" id="uiPathInputtooltip">
<span>{"Press 'Enter' to change the path for test files."}</span>
</Tooltip>
}
>
Create
</button>
<input
list="utPathList"
className="inputFolder custom-select"
id="utPath"
data-id="uiPathInput"
name="utPath"
value={inputPathValue}
style={{ backgroundImage: "var(--primary)" }}
onKeyDown={() => { if (inputPathValue === '/') setInputPathValue('')} }
onChange={handleTestDirInput}
onClick = {() => { if (inputPathValue === '/') setInputPathValue('')} }
/>
</OverlayTrigger>
<OverlayTrigger
placement="top-end"
overlay={
<Tooltip className="text-nowrap" id="uiPathInputButtontooltip">
<span>Create a test folder</span>
</Tooltip>
}
>
<button
className="btn border ml-2"
data-id="testTabGenerateTestFolder"
disabled={disableCreateButton}
onClick={handleCreateFolder}
>
Create
</button>
</OverlayTrigger>
</div>
</div>
</div>
<div>
<div className="d-flex p-2">
<button
className="btn border w-50"
data-id="testTabGenerateTestFile"
title="Generate a sample test file"
disabled={disableGenerateButton}
onClick={async () => {
await testTabLogic.generateTestFile((err:any) => { if (err) setToasterMsg(err)}) // eslint-disable-line @typescript-eslint/no-explicit-any
await updateForNewCurrent()
}}
>
Generate
</button>
<a className="btn border text-decoration-none pr-0 d-flex w-50 ml-2" title="Check out documentation." target="__blank" href="https://remix-ide.readthedocs.io/en/latest/unittesting.html#test-directory">
<OverlayTrigger overlay={
<Tooltip id="generateTestsButtontooltip" className="text-nowrap">
<span>Generate a sample test file</span>
</Tooltip>
} placement={'bottom-start'}>
<button
className="btn border w-50"
data-id="testTabGenerateTestFile"
disabled={disableGenerateButton}
onClick={async () => {
await testTabLogic.generateTestFile((err:any) => { if (err) setToasterMsg(err)}) // eslint-disable-line @typescript-eslint/no-explicit-any
await updateForNewCurrent()
}}
>
Generate
</button>
</OverlayTrigger>
<OverlayTrigger overlay={
<Tooltip id="generateTestsLinktooltip" className="text-nowrap">
<span>Check out documentation.</span>
</Tooltip>
} placement={'bottom-start'}>
<a className="btn border text-decoration-none pr-0 d-flex w-50 ml-2" target="__blank" href="https://remix-ide.readthedocs.io/en/latest/unittesting.html#test-directory">
<label className="btn p-1 ml-2 m-0">How to use...</label>
</a>
</OverlayTrigger>
</div>
<div className="d-flex p-2">
<button id="runTestsTabRunAction" title={runButtonTitle} data-id="testTabRunTestsTabRunAction" className="w-50 btn btn-primary" disabled={disableRunButton} onClick={runTests}>
<span className="fas fa-play ml-2"></span>
<label className="labelOnBtn btn btn-primary p-1 ml-2 m-0">Run</label>
</button>
<button id="runTestsTabStopAction" data-id="testTabRunTestsTabStopAction" className="w-50 pl-2 ml-2 btn btn-secondary" disabled={disableStopButton} title="Stop running tests" onClick={stopTests}>
<span className="fas fa-stop ml-2"></span>
<label className="labelOnBtn btn btn-secondary p-1 ml-2 m-0" id="runTestsTabStopActionLabel">{stopButtonLabel}</label>
<OverlayTrigger placement={'top-start'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>
{runButtonTitle}
</span>
</Tooltip>
}>
<button id="runTestsTabRunAction"data-id="testTabRunTestsTabRunAction" className="w-50 btn btn-primary" disabled={disableRunButton} onClick={runTests}>
<span className="fas fa-play ml-2"></span>
<label className="labelOnBtn btn btn-primary p-1 ml-2 m-0">Run</label>
</button>
</OverlayTrigger>
<button id="runTestsTabStopAction" data-id="testTabRunTestsTabStopAction" className="w-50 pl-2 ml-2 btn btn-secondary" disabled={disableStopButton} onClick={stopTests}>
<OverlayTrigger placement={'top-start'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>
Stop running tests
</span>
</Tooltip>
}>
<span>
<span className="fas fa-stop ml-2"></span>
<label className="labelOnBtn btn btn-secondary p-1 ml-2 m-0" id="runTestsTabStopActionLabel">{stopButtonLabel}</label>
</span>
</OverlayTrigger>
</button>
</div>
<div className="d-flex align-items-center mx-3 pb-2 mt-2 border-bottom">
@ -730,7 +786,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const elemId = `singleTest${testFileObj.fileName}`
return (
<div className="d-flex align-items-center py-1" key={index}>
<input className="singleTest" id={elemId} onChange={(e) => toggleCheckbox(e.target.checked, index)} type="checkbox" checked={testFileObj.checked} />
<input data-id="singleTest" className="singleTest" id={elemId} onChange={(e) => toggleCheckbox(e.target.checked, index)} type="checkbox" checked={testFileObj.checked} />
<label className="singleTestLabel text-nowrap pl-2 mb-0" htmlFor={elemId}>{testFileObj.fileName}</label>
</div>
)

@ -1,4 +1,5 @@
import React from 'react' //eslint-disable-line
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
interface StaticAnalyserButtonProps {
onClick: (event) => void
@ -14,10 +15,21 @@ const StaticAnalyserButton = ({
title
}: StaticAnalyserButtonProps) => {
let classList = "btn btn-sm w-25 btn-primary"
classList += disabled ? " disabled" : ""
classList += disabled ? " disabled" : ""
return (
<button className={classList} disabled={disabled} title={title} onClick={onClick}>
{buttonText}
<button className={classList} disabled={disabled} onClick={onClick}>
<OverlayTrigger
placement="bottom-start"
overlay={
<Tooltip id="ssaRunButtonTooltip" className="text-nowrap">
<span>{title}</span>
</Tooltip>
}
>
<span>
{buttonText}
</span>
</OverlayTrigger>
</button>
)
}

@ -255,7 +255,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
if(fileName !== currentFile) {
const {file, provider} = await props.analysisModule.call('fileManager', 'getPathFromUrl', fileName)
if (file.startsWith('.deps') || (provider.type === 'localhost' && file.startsWith('localhost/node_modules'))) isLibrary = true
}
}
const msg = message(result.name, item.warning, item.more, fileName, locationString)
const options = {
type: 'warning',
@ -321,7 +321,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
if(fileName !== currentFile) {
const {file, provider} = await props.analysisModule.call('fileManager', 'getPathFromUrl', fileName)
if (file.startsWith('.deps') || (provider.type === 'localhost' && file.startsWith('localhost/node_modules'))) isLibrary = true
}
}
const msg = message(item.title, item.description, item.more, fileName, locationString)
const options = {
type: 'warning',
@ -490,6 +490,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
label="Select all"
onClick={() => handleCheckAllModules(groupedModules)}
onChange={() => {}}
tooltipPlacement={'top-start'}
/>
<RemixUiCheckbox
id="autorunstaticanalysis"
@ -499,6 +500,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
checked={autoRun}
label="Autorun"
onChange={() => {}}
tooltipPlacement={'bottom-start'}
/>
<Button
buttonText="Run"

@ -1,36 +1,42 @@
import React, { useState, useEffect } from 'react' //eslint-disable-line
import { useIntl } from 'react-intl'
import { FormattedMessage } from 'react-intl'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Placement } from 'react-bootstrap/esm/Overlay'
import { FileExplorerMenuProps } from '../types'
const _paq = window._paq = window._paq || []
export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
const intl = useIntl()
const [state, setState] = useState({
menuItems: [
{
action: 'createNewFile',
title: 'Create New File',
icon: 'far fa-file'
icon: 'far fa-file',
placement: 'top-start'
},
{
action: 'createNewFolder',
title: 'Create New Folder',
icon: 'far fa-folder'
icon: 'far fa-folder',
placement: 'top-end'
},
{
action: 'publishToGist',
title: 'Publish all the current workspace files (only root) to a github gist',
icon: 'fab fa-github'
icon: 'fab fa-github',
placement: 'top-start'
},
{
action: 'uploadFile',
title: 'Load a local file into current workspace',
icon: 'fa fa-upload'
icon: 'fa fa-upload',
placement: 'right'
},
{
action: 'updateGist',
title: 'Update the current [gist] explorer',
icon: 'fab fa-github'
icon: 'fab fa-github',
placement: 'right-start'
}
].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action })),
actions: {}
@ -48,49 +54,74 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
return (
<>
<span className='remixui_label' title={props.title} data-path={props.title} style={{ fontWeight: 'bold' }}>{ props.title }</span>
<OverlayTrigger
placement="top-start"
overlay={
<Tooltip id="remixuilabelTooltip" className="text-nowrap">
<span>{props.title}</span>
</Tooltip>
}
>
<span className='remixui_label' data-path={props.title} style={{ fontWeight: 'bold' }}>{ props.title }</span>
</OverlayTrigger>
<span className="pl-2">{
state.menuItems.map(({ action, title, icon }, index) => {
state.menuItems.map(({ action, title, icon, placement }, index) => {
if (action === 'uploadFile') {
return (
<label
id={action}
data-id={'fileExplorerUploadFile' + action }
className={icon + ' mb-0 remixui_newFile'}
title={intl.formatMessage({id: `filePanel.${action}`, defaultMessage: title})}
key={index}
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="uploadFileTooltip" className="text-nowrap">
<span><FormattedMessage id={`filePanel.${action}`} defaultMessage={title} /></span>
</Tooltip>
}
>
<input id="fileUpload" data-id="fileExplorerFileUpload" type="file" onChange={(e) => {
e.stopPropagation()
props.uploadFile(e.target)
e.target.value = null
}}
multiple />
</label>
<label
id={action}
data-id={'fileExplorerUploadFile' + action }
className={icon + ' mb-0 remixui_newFile'}
key={index}
>
<input id="fileUpload" data-id="fileExplorerFileUpload" type="file" onChange={(e) => {
e.stopPropagation()
props.uploadFile(e.target)
e.target.value = null
}}
multiple />
</label>
</OverlayTrigger>
)
} else {
return (
<span
id={action}
data-id={'fileExplorerNewFile' + action}
onClick={(e) => {
e.stopPropagation()
_paq.push(['trackEvent', 'fileExplorer', 'fileAction', action])
if (action === 'createNewFile') {
props.createNewFile()
} else if (action === 'createNewFolder') {
props.createNewFolder()
} else if (action === 'publishToGist') {
props.publishToGist()
} else {
state.actions[action]()
}
}}
className={'newFile ' + icon + ' remixui_newFile'}
title={intl.formatMessage({id: `filePanel.${action}`, defaultMessage: title})}
key={index}
<OverlayTrigger
placement={placement as Placement}
overlay={
<Tooltip id={`${action}-${title}-${icon}-${index}`} className="text-nowrap">
<span><FormattedMessage id={`filePanel.${action}`} defaultMessage={title} /></span>
</Tooltip>
}
>
</span>
<span
id={action}
data-id={'fileExplorerNewFile' + action}
onClick={(e) => {
e.stopPropagation()
_paq.push(['trackEvent', 'fileExplorer', 'fileAction', action])
if (action === 'createNewFile') {
props.createNewFile()
} else if (action === 'createNewFolder') {
props.createNewFolder()
} else if (action === 'publishToGist') {
props.publishToGist()
} else {
state.actions[action]()
}
}}
className={'newFile ' + icon + ' remixui_newFile'}
key={`${action}-${title}-${index}`}
>
</span>
</OverlayTrigger>
)
}
})}

@ -103,7 +103,7 @@ export interface FileExplorerProps {
dispatchMoveFile: (src: string, dest: string) => Promise<void>,
dispatchMoveFolder: (src: string, dest: string) => Promise<void>
}
type Placement = import('react-overlays/usePopper').Placement
export interface FileExplorerMenuProps {
title: string,
menuItems: string[],
@ -111,6 +111,7 @@ export interface FileExplorerMenuProps {
createNewFolder: (parentFolder?: string) => void,
publishToGist: (path?: string) => void,
uploadFile: (target: EventTarget & HTMLInputElement) => void
tooltipPlacement?: Placement
}
export interface FileExplorerContextMenuProps {
actions: action[],

Loading…
Cancel
Save