Skip to content

Commit 01316af

Browse files
author
ci-bot
committed
local test
1 parent e6e404c commit 01316af

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed

apps/remix-ide-e2e/src/tests/importResolver.test.ts

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,114 @@ contract CommentedImports is ERC20 {
10651065
])
10661066
},
10671067

1068+
'Test complex local imports with external dependencies #group22': function (browser: NightwatchBrowser) {
1069+
browser
1070+
// Create a realistic project structure with multiple folders and contracts
1071+
.addFile('contracts/interfaces/IStorage.sol', localImportsProjectSource['contracts/interfaces/IStorage.sol'])
1072+
.addFile('contracts/libraries/Math.sol', localImportsProjectSource['contracts/libraries/Math.sol'])
1073+
.addFile('contracts/base/BaseContract.sol', localImportsProjectSource['contracts/base/BaseContract.sol'])
1074+
.addFile('contracts/TokenVault.sol', localImportsProjectSource['contracts/TokenVault.sol'])
1075+
.addFile('contracts/main/Staking.sol', localImportsProjectSource['contracts/main/Staking.sol'])
1076+
// Enable generate-contract-metadata to verify compilation artifacts
1077+
.waitForElementVisible('*[data-id="topbar-settingsIcon"]')
1078+
.click('*[data-id="topbar-settingsIcon"]')
1079+
.waitForElementVisible('*[data-id="settings-sidebar-general"]')
1080+
.click('*[data-id="settings-sidebar-general"]')
1081+
.waitForElementPresent('[data-id="generate-contract-metadataSwitch"]')
1082+
.click('[data-id="generate-contract-metadataSwitch"]')
1083+
// Open the main contract which imports everything
1084+
.openFile('contracts/main/Staking.sol')
1085+
// Switch to Solidity compiler panel
1086+
.clickLaunchIcon('solidity')
1087+
// Compile the contract
1088+
.click('[data-id="compilerContainerCompileBtn"]')
1089+
.pause(5000) // Longer pause for multiple external imports
1090+
.clickLaunchIcon('filePanel')
1091+
// Verify external dependencies were resolved
1092+
.waitForElementVisible('*[data-id="treeViewDivDraggableItem.deps"]', 60000)
1093+
.click('*[data-id="treeViewDivDraggableItem.deps"]')
1094+
.waitForElementVisible('*[data-id="treeViewDivDraggableItem.deps/npm"]', 60000)
1095+
.click('*[data-id="treeViewDivDraggableItem.deps/npm"]')
1096+
.waitForElementVisible('*[data-id="treeViewDivDraggableItem.deps/npm/@openzeppelin"]', 60000)
1097+
.click('*[data-id="treeViewDivDraggableItem.deps/npm/@openzeppelin"]')
1098+
.waitForElementVisible('*[data-id^="treeViewDivDraggableItem.deps/npm/@openzeppelin/contracts@"]', 60000)
1099+
.perform(function () {
1100+
browser.assert.ok(true, 'External OpenZeppelin dependencies should be resolved')
1101+
})
1102+
// Verify compilation succeeded with mixed local and external imports
1103+
.waitForElementPresent('*[data-id="compiledContracts"]', 10000)
1104+
.perform(function () {
1105+
browser.assert.ok(true, 'Complex project with local and external imports should compile successfully')
1106+
})
1107+
// Verify all local contracts are in the workspace (not in .deps)
1108+
.expandAllFolders()
1109+
.waitForElementVisible('*[data-id="treeViewDivDraggableItemcontracts/interfaces"]', 10000)
1110+
.waitForElementVisible('*[data-id="treeViewDivDraggableItemcontracts/libraries"]', 10000)
1111+
.waitForElementVisible('*[data-id="treeViewDivDraggableItemcontracts/base"]', 10000)
1112+
.waitForElementVisible('*[data-id="treeViewDivDraggableItemcontracts/main"]', 10000)
1113+
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/TokenVault.sol"]', 10000)
1114+
.perform(function () {
1115+
browser.assert.ok(true, 'All local contract folders should be present in workspace')
1116+
})
1117+
// Open resolution index to verify local imports are mapped correctly
1118+
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.deps/npm/.resolution-index.json"]', 60000)
1119+
.openFile('.deps/npm/.resolution-index.json')
1120+
.pause(1000)
1121+
.getEditorValue((content) => {
1122+
try {
1123+
const idx = JSON.parse(content)
1124+
const sourceFiles = Object.keys(idx || {})
1125+
1126+
// Find Staking.sol entry (main contract)
1127+
const stakingEntry = sourceFiles.find(file => file.includes('Staking.sol'))
1128+
browser.assert.ok(!!stakingEntry, 'Resolution index should contain Staking.sol')
1129+
1130+
if (stakingEntry) {
1131+
const mappings = idx[stakingEntry]
1132+
const mappingKeys = Object.keys(mappings)
1133+
1134+
// Verify that local imports are NOT in the mappings (they should be direct)
1135+
const hasLocalImport = mappingKeys.some(key =>
1136+
key.includes('../base/BaseContract.sol') ||
1137+
key.includes('../TokenVault.sol')
1138+
)
1139+
browser.assert.ok(!hasLocalImport, 'Local relative imports should not be in resolution index')
1140+
1141+
// Verify that external imports ARE in the mappings
1142+
const hasExternalImport = mappingKeys.some(key =>
1143+
key.includes('@openzeppelin/contracts')
1144+
)
1145+
browser.assert.ok(hasExternalImport, 'External imports should be mapped in resolution index')
1146+
}
1147+
} catch (e) {
1148+
browser.assert.fail('Resolution index should be valid JSON: ' + e.message)
1149+
}
1150+
})
1151+
// Verify build-info artifacts contain both local and external contracts
1152+
.verifyArtifactsBuildInfo([
1153+
{
1154+
packagePath: 'contracts/main/Staking.sol',
1155+
versionComment: 'SPDX-License-Identifier: MIT',
1156+
description: 'Should find local Staking.sol contract in build-info'
1157+
},
1158+
{
1159+
packagePath: 'contracts/base/BaseContract.sol',
1160+
versionComment: 'SPDX-License-Identifier: MIT',
1161+
description: 'Should find local BaseContract.sol in build-info'
1162+
},
1163+
{
1164+
packagePath: '@openzeppelin/contracts',
1165+
versionComment: 'Ownable.sol',
1166+
description: 'Should find external OpenZeppelin Ownable.sol in build-info'
1167+
},
1168+
{
1169+
packagePath: '@openzeppelin/contracts',
1170+
versionComment: 'Pausable.sol',
1171+
description: 'Should find external OpenZeppelin Pausable.sol in build-info'
1172+
}
1173+
])
1174+
},
1175+
10681176
}
10691177

10701178
// Named source objects for each test group
@@ -1735,6 +1843,164 @@ contract ChainlinkMultiVersion {
17351843
}
17361844
}
17371845

1846+
const localImportsProjectSource = {
1847+
'contracts/interfaces/IStorage.sol': {
1848+
content: `// SPDX-License-Identifier: MIT
1849+
pragma solidity ^0.8.20;
1850+
1851+
/**
1852+
* @title IStorage
1853+
* @dev Interface for storage operations
1854+
*/
1855+
interface IStorage {
1856+
function store(uint256 value) external;
1857+
function retrieve() external view returns (uint256);
1858+
}
1859+
`
1860+
},
1861+
'contracts/libraries/Math.sol': {
1862+
content: `// SPDX-License-Identifier: MIT
1863+
pragma solidity ^0.8.20;
1864+
1865+
/**
1866+
* @title Math
1867+
* @dev Basic math operations library
1868+
*/
1869+
library Math {
1870+
function add(uint256 a, uint256 b) internal pure returns (uint256) {
1871+
return a + b;
1872+
}
1873+
1874+
function multiply(uint256 a, uint256 b) internal pure returns (uint256) {
1875+
return a * b;
1876+
}
1877+
}
1878+
`
1879+
},
1880+
'contracts/base/BaseContract.sol': {
1881+
content: `// SPDX-License-Identifier: MIT
1882+
pragma solidity ^0.8.20;
1883+
1884+
// Local import from interfaces folder
1885+
import "../interfaces/IStorage.sol";
1886+
1887+
// External import from OpenZeppelin
1888+
import "@openzeppelin/contracts/access/Ownable.sol";
1889+
1890+
/**
1891+
* @title BaseContract
1892+
* @dev Base contract with storage and access control
1893+
*/
1894+
abstract contract BaseContract is IStorage, Ownable {
1895+
uint256 private storedValue;
1896+
1897+
constructor() Ownable(msg.sender) {}
1898+
1899+
function store(uint256 value) external override onlyOwner {
1900+
storedValue = value;
1901+
}
1902+
1903+
function retrieve() external view override returns (uint256) {
1904+
return storedValue;
1905+
}
1906+
}
1907+
`
1908+
},
1909+
'contracts/TokenVault.sol': {
1910+
content: `// SPDX-License-Identifier: MIT
1911+
pragma solidity ^0.8.20;
1912+
1913+
// Local import from libraries
1914+
import "./libraries/Math.sol";
1915+
1916+
// External imports from OpenZeppelin
1917+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
1918+
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
1919+
1920+
/**
1921+
* @title TokenVault
1922+
* @dev Manages ERC20 token deposits
1923+
*/
1924+
contract TokenVault {
1925+
using SafeERC20 for IERC20;
1926+
using Math for uint256;
1927+
1928+
mapping(address => mapping(address => uint256)) public deposits;
1929+
1930+
event Deposited(address indexed user, address indexed token, uint256 amount);
1931+
1932+
function deposit(address token, uint256 amount) external {
1933+
require(amount > 0, "Amount must be greater than 0");
1934+
1935+
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
1936+
deposits[msg.sender][token] = Math.add(deposits[msg.sender][token], amount);
1937+
1938+
emit Deposited(msg.sender, token, amount);
1939+
}
1940+
1941+
function getDeposit(address user, address token) external view returns (uint256) {
1942+
return deposits[user][token];
1943+
}
1944+
}
1945+
`
1946+
},
1947+
'contracts/main/Staking.sol': {
1948+
content: `// SPDX-License-Identifier: MIT
1949+
pragma solidity ^0.8.20;
1950+
1951+
// Local imports - relative paths from different folders
1952+
import "../base/BaseContract.sol";
1953+
import "../TokenVault.sol";
1954+
import "../libraries/Math.sol";
1955+
1956+
// External imports from OpenZeppelin
1957+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
1958+
import "@openzeppelin/contracts/utils/Pausable.sol";
1959+
1960+
/**
1961+
* @title Staking
1962+
* @dev Main staking contract that combines local and external dependencies
1963+
*/
1964+
contract Staking is BaseContract, Pausable {
1965+
using Math for uint256;
1966+
1967+
TokenVault public vault;
1968+
IERC20 public stakingToken;
1969+
1970+
mapping(address => uint256) public stakedBalance;
1971+
1972+
event Staked(address indexed user, uint256 amount);
1973+
1974+
constructor(address _stakingToken, address _vault) {
1975+
stakingToken = IERC20(_stakingToken);
1976+
vault = TokenVault(_vault);
1977+
}
1978+
1979+
function stake(uint256 amount) external whenNotPaused {
1980+
require(amount > 0, "Cannot stake 0");
1981+
1982+
stakingToken.transferFrom(msg.sender, address(this), amount);
1983+
stakedBalance[msg.sender] = Math.add(stakedBalance[msg.sender], amount);
1984+
1985+
emit Staked(msg.sender, amount);
1986+
}
1987+
1988+
function getStakedBalance(address user) external view returns (uint256) {
1989+
return stakedBalance[user];
1990+
}
1991+
1992+
function pause() external onlyOwner {
1993+
_pause();
1994+
}
1995+
1996+
function unpause() external onlyOwner {
1997+
_unpause();
1998+
}
1999+
}
2000+
`
2001+
}
2002+
}
2003+
17382004
// Keep sources array for backwards compatibility with @sources function
17392005
const sources = [
17402006
upgradeableNFTSource,
@@ -1761,5 +2027,6 @@ const sources = [
17612027
jsDelivrMultiVersionSource,
17622028
jsDelivrV5WithV4UtilsSource,
17632029
chainlinkMultiVersionSource,
2030+
localImportsProjectSource,
17642031
]
17652032

0 commit comments

Comments
 (0)