diff --git a/.circleci/config.yml b/.circleci/config.yml index a23534de7b7..0584c5a24d5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ parameters: type: boolean default: false orbs: - browser-tools: circleci/browser-tools@1.5.2 + browser-tools: circleci/browser-tools@1.5.3 win: circleci/windows@5.0 node: circleci/node@7.0.0 jobs: @@ -125,6 +125,9 @@ jobs: - node/install: install-yarn: true node-version: '20.2' + - restore_cache: + keys: + - remixdesktop-linux-deps-{{ checksum "apps/remixdesktop/yarn.lock" }} - run: command: | node -v @@ -134,10 +137,14 @@ jobs: mkdir apps/remixdesktop/build cp -r dist/apps/remix-ide apps/remixdesktop/build cd apps/remixdesktop/ - yarn add node-pty - yarn --ignore-optional - yarn add @remix-project/remix-ws-templates + yarn add node-pty --network-timeout 600000 + yarn --ignore-optional --network-timeout 600000 + yarn add @remix-project/remix-ws-templates --network-timeout 600000 ./rundist.bash test_only + - save_cache: + key: remixdesktop-linux-deps-{{ checksum "apps/remixdesktop/yarn.lock" }} + paths: + - apps/remixdesktop/node_modules - run: name: "Run tests" command: | @@ -152,6 +159,9 @@ jobs: path: ./apps/remixdesktop/reports/tests - store_artifacts: path: ./apps/remixdesktop/reports/screenshots + - store_artifacts: + path: /home/circleci/remix-project/logs + destination: logs build-remixdesktop-linux: machine: @@ -310,6 +320,9 @@ jobs: path: ./apps/remixdesktop/reports/tests - store_artifacts: path: ./apps/remixdesktop/reports/screenshots + - store_artifacts: + path: /home/circleci/remix-project/logs + destination: logs # to setup a new certificate: # 1) from https://one.digicert.com/signingmanager/dashboard in the 'get started' section, the following configuration variables can be created: @@ -581,6 +594,9 @@ jobs: path: ./apps/remixdesktop/reports/tests - store_artifacts: path: ./apps/remixdesktop/reports/screenshots + - store_artifacts: + path: /home/circleci/remix-project/logs + destination: logs uploadartifacts: docker: @@ -662,6 +678,11 @@ jobs: default: 1 parallelism: << parameters.parallelism >> steps: + - run: + name: set env + command: | + export DBUS_SESSION_BUS_ADDRESS=/dev/null + export XDG_RUNTIME_DIR=/tmp - checkout - attach_workspace: at: . @@ -679,21 +700,25 @@ jobs: - browser-tools/install-browser-tools: install-firefox: false install-chrome: true - install-chromedriver: false + install-chromedriver: true install-geckodriver: false - - run: yarn install_webdriver - - run: google-chrome --version - when: condition: equal: [ "chromeMetamask", << parameters.browser >> ] steps: + - run: + name: Set metamask test flag + command: echo "METAMASK_TEST=true" >> $BASH_ENV - browser-tools/install-browser-tools: install-firefox: false install-chrome: true - install-chromedriver: false + install-chromedriver: true install-geckodriver: false - - run: yarn install_webdriver - - run: google-chrome --version + #- run: yarn install_webdriver + - run: + command: | + google-chrome --version + chromedriver --version - when: condition: equal: [ "firefox", << parameters.browser >> ] @@ -710,6 +735,9 @@ jobs: path: ./reports/tests - store_artifacts: path: ./reports/screenshots + - store_artifacts: + path: /home/circleci/remix-project/logs + destination: logs tests-passed: machine: @@ -743,14 +771,20 @@ jobs: install-firefox: false install-chrome: true install-geckodriver: false - install-chromedriver: false - - run: yarn install_webdriver - - run: google-chrome --version + install-chromedriver: true + #- run: yarn install_webdriver + - run: + command: | + google-chrome --version + chromedriver --version - run: ./apps/remix-ide/ci/browser_test_plugin.sh << parameters.plugin >> - store_test_results: path: ./reports/tests - store_artifacts: path: ./reports/screenshots + - store_artifacts: + path: /home/circleci/remix-project/logs + destination: logs predeploy: @@ -812,7 +846,7 @@ workflows: script: ["flaky.sh"] job: ["nogroup"] jobsize: ["1"] - parallelism: [5] + parallelism: [10] run_metamask_tests: when: << pipeline.parameters.run_metamask_tests >> jobs: @@ -868,6 +902,9 @@ workflows: - test-remixdesktop-linux: requires: - build-desktop + filters: + branches: + only: [/.*desktop.*/] - test-remixdesktop-windows: requires: - build-desktop @@ -915,8 +952,8 @@ workflows: parameters: browser: ["chrome", "firefox"] script: ["browser_test.sh"] - job: ["0","1","2","3","4","5","6","7","8","9"] - jobsize: ["10"] + job: ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14"] + jobsize: ["15"] parallelism: [15] - remix-ide-browser: requires: diff --git a/apps/remix-ide-e2e/chrome-driver.sh b/apps/remix-ide-e2e/chrome-driver.sh new file mode 100755 index 00000000000..c0ccd0f2acf --- /dev/null +++ b/apps/remix-ide-e2e/chrome-driver.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# Usage: chrome-driver.sh [install_dir] +# Determine install directory from first argument or ORB_PARAM_DRIVER_INSTALL_DIR +INSTALL_DIR=${1:-${ORB_PARAM_DRIVER_INSTALL_DIR:-"./tmp/webdrivers"}} +echo "Installing ChromeDriver into $INSTALL_DIR" + +# Determine the OS platform +OS="$(uname)" + +if [ "$OS" == "Darwin" ]; then + # macOS systems + if [ -e "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ]; then + version=$("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --version) + echo "Google Chrome version on macOS: $version" + else + echo "Google Chrome is not installed on your macOS." + fi +elif [ "$OS" == "Linux" ]; then + # Linux systems + if command -v google-chrome >/dev/null; then + version=$(google-chrome --version) + echo "Google Chrome version on Linux: $version" + else + echo "Google Chrome is not installed on your Linux." + # exit without error + exit 0 + fi +else + echo "Unsupported OS." + exit 0 +fi + +MAJORVERSION='135' # $(echo "$version" | grep -Eo '[0-9]+\.' | head -1 | cut -d'.' -f1) +echo "CHROME DRIVER INSTALL $MAJORVERSION" + +# Determine target platform for ChromeDriver download +case "$OS" in + Darwin) + if [[ "$(uname -m)" == "arm64" ]]; then + PLATFORM="mac-arm64" + else + PLATFORM="mac-x64" + fi + ;; + Linux) + PLATFORM="linux64" + ;; + *) + echo "Unsupported OS: $OS"; exit 1 + ;; +esac +echo "Detected platform for download: $PLATFORM" + +# Determine ChromeDriver version and download URL +if [ "$MAJORVERSION" -lt 115 ]; then + # Chrome <115: use storage.googleapis.com + CHROMEDRIVER_VERSION=$(curl -sS "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_${MAJORVERSION}" | tr -d '\r') + CHROMEDRIVER_DOWNLOAD_URL="https://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_${PLATFORM}.zip" +else + # Chrome >=115: use Chrome for Testing JSON feed + FEED_URL="https://googlechromelabs.github.io/chrome-for-testing/latest-versions-per-milestone-with-downloads.json" + CHROMEDRIVER_VERSION=$(curl -sS "$FEED_URL" | jq -r ".milestones.\"$MAJORVERSION\".version") + CHROMEDRIVER_DOWNLOAD_URL=$(curl -sS "$FEED_URL" \ + | jq -r ".milestones.\"$MAJORVERSION\".downloads.chromedriver[] \ + | select(.platform==\"$PLATFORM\").url") +fi + +echo "Matching ChromeDriver version: $CHROMEDRIVER_VERSION" +echo "Downloading ChromeDriver from $CHROMEDRIVER_DOWNLOAD_URL" + +# Prepare install directory +mkdir -p "$INSTALL_DIR" + +# Download and install ChromeDriver +ZIP_PATH="${INSTALL_DIR}/chromedriver_${PLATFORM}.zip" +curl -sS -o "$ZIP_PATH" "$CHROMEDRIVER_DOWNLOAD_URL" +unzip -o "$ZIP_PATH" -d "$INSTALL_DIR" + +# Move the extracted chromedriver binary to the root of INSTALL_DIR +EXTRACTED_DIR=$(find "$INSTALL_DIR" -mindepth 1 -maxdepth 1 -type d | head -n1) +if [ -f "$EXTRACTED_DIR/chromedriver" ]; then + mv "$EXTRACTED_DIR/chromedriver" "$INSTALL_DIR/chromedriver" +else + echo "Error: chromedriver binary not found in $EXTRACTED_DIR" + exit 1 +fi + +chmod +x "$INSTALL_DIR/chromedriver" +# Cleanup extracted directory and zip +rm -rf "$EXTRACTED_DIR" +rm -f "$ZIP_PATH" + +echo "ChromeDriver installed at $INSTALL_DIR/chromedriver" \ No newline at end of file diff --git a/apps/remix-ide-e2e/install-webdriver.sh b/apps/remix-ide-e2e/install-webdriver.sh index 3dec4bf38ee..6f8f2cf3fec 100644 --- a/apps/remix-ide-e2e/install-webdriver.sh +++ b/apps/remix-ide-e2e/install-webdriver.sh @@ -1,31 +1,7 @@ #!/bin/bash -# Determine the OS platform -OS="$(uname)" - -if [ "$OS" == "Darwin" ]; then - # macOS systems - if [ -e "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ]; then - version=$("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --version) - echo "Google Chrome version on macOS: $version" - else - echo "Google Chrome is not installed on your macOS." - fi -elif [ "$OS" == "Linux" ]; then - # Linux systems - if command -v google-chrome >/dev/null; then - version=$(google-chrome --version) - echo "Google Chrome version on Linux: $version" - else - echo "Google Chrome is not installed on your Linux." - fi -else - echo "Unsupported OS." -fi - -MAJORVERSION=$(echo "$version" | grep -Eo '[0-9]+\.' | head -1 | cut -d'.' -f1) -echo "CHROME DRIVER INSTALL $MAJORVERSION" - +set -e +./apps/remix-ide-e2e/chrome-driver.sh # Specify the directory to check directory="./tmp/webdrivers" @@ -39,4 +15,4 @@ fi yarn init -y --cwd "$directory" || exit 1 -yarn add -D chromedriver@$MAJORVERSION geckodriver --cwd "$directory" || yarn add -D chromedriver@$MAJORVERSION geckodriver --cwd "$directory" || yarn add -D chromedriver geckodriver --cwd "$directory" || exit 1 +yarn add -D geckodriver --cwd "$directory" || yarn add -D geckodriver --cwd "$directory" || yarn add -D chromedriver geckodriver --cwd "$directory" || exit 1 diff --git a/apps/remix-ide-e2e/nightwatch-chrome.ts b/apps/remix-ide-e2e/nightwatch-chrome.ts index 0e23274ef5d..b3993f0e728 100644 --- a/apps/remix-ide-e2e/nightwatch-chrome.ts +++ b/apps/remix-ide-e2e/nightwatch-chrome.ts @@ -10,8 +10,13 @@ module.exports = { webdriver: { start_process: true, - port: 9515, - server_path: './tmp/webdrivers/node_modules/chromedriver/bin/chromedriver', + port: 0, // pick a random free port + server_path: process.env.CI ? '/usr/local/bin/chromedriver' : './tmp/webdrivers/chromedriver', + host: '127.0.0.1', // bind only to IPv4 + cli_args: ['--host=127.0.0.1'], // ensure chromedriver itself uses IPv4 + status_poll_interval: 500, // wait 500 ms between /status checks + max_status_poll_tries: 60, // try up to 60 times → 30 s total wait + process_create_timeout: 300000, // overall 5 min timeout for process startup }, test_settings: { @@ -30,6 +35,32 @@ module.exports = { }, 'chrome': { + webdriver: { + "keep_alive": { "enabled": true, "keepAliveMsecs": 60000 }, + timeout_options: { timeout: 30000, retry_attempts: 10 } + }, + desiredCapabilities: { + 'browserName': 'chrome', + 'javascriptEnabled': true, + 'acceptSslCerts': true, + 'goog:chromeOptions': { + args: [ + 'window-size=2560,1440', + '--headless=new', + '--verbose', + '--disable-dev-shm-usage', + '--disable-gpu', + '--remote-debugging-pipe', + '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36' + ] + } + , pageLoadStrategy: 'eager', + timeouts: { pageLoad: 30000 } // e.g. 30 s max + } + }, + + // at the bottom of test_settings + 'chromeIdleTest': { desiredCapabilities: { 'browserName': 'chrome', 'javascriptEnabled': true, @@ -37,7 +68,6 @@ module.exports = { 'goog:chromeOptions': { args: [ 'window-size=2560,1440', - '--no-sandbox', '--headless=new', '--verbose', '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36' @@ -52,7 +82,7 @@ module.exports = { 'javascriptEnabled': true, 'acceptSslCerts': true, 'goog:chromeOptions': { - args: ['window-size=2560,1440', 'start-fullscreen', '--no-sandbox', '--verbose'] + args: ['window-size=2560,1440', 'start-fullscreen', '--verbose'] } } }, @@ -66,7 +96,6 @@ module.exports = { args: [ `--load-extension=${metamaskExtensionPath}`, '--window-size=2560,1440', - '--no-sandbox', '--verbose', '--disable-gpu', ] @@ -85,7 +114,6 @@ module.exports = { '--window-size=2560,1440', '--no-sandbox', '--verbose', - '--headless=new', '--disable-gpu', ] } @@ -98,7 +126,7 @@ module.exports = { 'javascriptEnabled': true, 'acceptSslCerts': true, 'goog:chromeOptions': { - args: ['window-size=2560,1440', 'start-fullscreen', '--no-sandbox', '--headless', '--verbose'], + args: ['window-size=2560,1440', 'start-fullscreen', '--headless', '--verbose'], extensions: [] } } diff --git a/apps/remix-ide-e2e/src/commands/renamePath.ts b/apps/remix-ide-e2e/src/commands/renamePath.ts index e87df0cb95d..86e5b5ec6c0 100644 --- a/apps/remix-ide-e2e/src/commands/renamePath.ts +++ b/apps/remix-ide-e2e/src/commands/renamePath.ts @@ -35,9 +35,13 @@ function renamePath(browser: NightwatchBrowser, path: string, newFileName: strin try { browser .click('#menuitemrename') + .saveScreenshot('./reports/screenshots/renamePath.png') .sendKeys('[data-input-path="' + path + '"]', newFileName) + .saveScreenshot('./reports/screenshots/renamePath1.png') .sendKeys('[data-input-path="' + path + '"]', browser.Keys.ENTER) + .saveScreenshot('./reports/screenshots/renamePath2.png') .waitForElementNotPresent('[data-path="' + path + '"]') + .saveScreenshot('./reports/screenshots/renamePath3.png') .waitForElementPresent('[data-path="' + renamedPath + '"]'); } catch (error) { console.error('An error occurred:', error.message); diff --git a/apps/remix-ide-e2e/src/helpers/init.ts b/apps/remix-ide-e2e/src/helpers/init.ts index 809824bd9ac..d57cd7d9b14 100644 --- a/apps/remix-ide-e2e/src/helpers/init.ts +++ b/apps/remix-ide-e2e/src/helpers/init.ts @@ -13,7 +13,6 @@ type LoadPlugin = { export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true, loadPlugin?: LoadPlugin, hideToolTips: boolean = true): void { browser .url(url || 'http://127.0.0.1:8080') - .pause(5000) .switchBrowserTab(0) .hidePopupPanel() .perform((done) => { diff --git a/apps/remix-ide-e2e/src/tests/metamask.test.ts b/apps/remix-ide-e2e/src/tests/metamask.test.ts index cab7fdabb35..c1ef06d0856 100644 --- a/apps/remix-ide-e2e/src/tests/metamask.test.ts +++ b/apps/remix-ide-e2e/src/tests/metamask.test.ts @@ -307,7 +307,8 @@ const tests = { } const branch = process.env.CIRCLE_BRANCH -const runTestsConditions = branch && (branch === 'master' || branch === 'remix_live' || branch.includes('remix_beta') || branch.includes('metamask')) +const metamaskEnv = process.env.METAMASK_TEST === 'true' +const runTestsConditions = (branch && (branch === 'master' || branch === 'remix_live' || branch.includes('remix_beta') || branch.includes('metamask'))) || metamaskEnv if (!checkBrowserIsChrome(browser)) { module.exports = {} diff --git a/apps/remix-ide-e2e/src/tests/remixd.test.ts b/apps/remix-ide-e2e/src/tests/remixd.test.ts index 0075cf7d884..3b53a2e9d61 100644 --- a/apps/remix-ide-e2e/src/tests/remixd.test.ts +++ b/apps/remix-ide-e2e/src/tests/remixd.test.ts @@ -419,23 +419,46 @@ async function installRemixd(): Promise { }) } -async function spawnRemixd(path: string): Promise { - console.log('spawnRemixd', path) - await installRemixd() - const remixd = spawn('chmod +x dist/libs/remixd/src/bin/remixd.js && dist/libs/remixd/src/bin/remixd.js --remix-ide http://127.0.0.1:8080', [`-s ${path}`], { cwd: process.cwd(), shell: true, detached: true }) +export function spawnRemixd(workspacePath: string): Promise { + + const remixd = spawn( + 'chmod +x dist/libs/remixd/src/bin/remixd.js && dist/libs/remixd/src/bin/remixd.js --remix-ide http://127.0.0.1:8080', + [`-s ${workspacePath}`], + { cwd: process.cwd(), shell: true, detached: true } + ) + + // Pipe stdout and stderr to log + remixd.stdout.on('data', (data) => { + const text = data.toString() + console.log(`[stdout] ${text}`) + }) + + remixd.stderr.on('data', (data) => { + const text = data.toString() + console.log(`[stderr] ${text}`) + }) + + // Log exit + remixd.on('exit', (code, signal) => { + console.log(`remixd exited with code ${code}, signal ${signal}\n`) + }) + + // Handle startup and resolve when ready return new Promise((resolve, reject) => { - remixd.stdout.on('data', function (data) { + remixd.stdout.on('data', (data) => { + const text = data.toString() + console.log(`[remixd] ${text}`) if ( - data.toString().includes('is listening') - || data.toString().includes('There is already a client running') + text.includes('is listening') || + text.includes('There is already a client running') ) { - resolve(remixd) } }) - remixd.stderr.on('err', function (data) { - console.log(data.toString()) - reject(data.toString()) + + remixd.on('error', (err) => { + console.log(`remixd error: ${err.message}\n`) + reject(err) }) }) } @@ -527,7 +550,7 @@ async function installFoundry(): Promise { server.stdout.on('data', function (data) { console.log(data.toString()) if ( - data.toString().includes("foundryup: use - ") + data.toString().includes("use - chisel") ) { console.log('resolving') resolve() diff --git a/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts b/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts index 4b69ff2b774..796b367ed45 100644 --- a/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts +++ b/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts @@ -454,6 +454,7 @@ module.exports = { console.log('Test Fork Mainnet', address) addressRef = address }) + .clearConsole() // from Mainnet fork 2, check that block number is at `currentBlockNumber` + 4 .clickFunction('checkOrigin - transact (not payable)', { types: 'uint256 incr', values: '3'}) .perform((done) => { diff --git a/apps/remix-ide/ci/browser_test.sh b/apps/remix-ide/ci/browser_test.sh index ed1e968109e..c39bca7b5ad 100755 --- a/apps/remix-ide/ci/browser_test.sh +++ b/apps/remix-ide/ci/browser_test.sh @@ -2,15 +2,57 @@ set -e +# if $1 is chrome +# if [ "$1" == "chrome" ]; then +# # 1) Start ChromeDriver in the background +# /usr/local/bin/chromedriver \ +# --port=9515 \ +# --host=127.0.0.1 \ +# --silent > driver.log 2>&1 & +# # Save its PID so you can kill it later if needed +# echo $! > chromedriver.pid + +# # 2) Wait until ChromeDriver is actually listening +# while ! curl -s http://127.0.0.1:9515/status >/dev/null; do +# sleep 0.2 +# done + +# echo "🚀 ChromeDriver is up on 127.0.0.1:9515" + +# fi + +export DBUS_SESSION_BUS_ADDRESS=/dev/null +export XDG_RUNTIME_DIR=/tmp + BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}} echo "$BUILD_ID" TEST_EXITCODE=0 npx ganache & npx http-server -p 9090 --cors='*' ./node_modules & yarn run serve:production & -sleep 5 + +# Wait for Ganache (default port 8545) via JSON-RPC +echo "Waiting for Ganache on port 8545 (eth_blockNumber)..." +until curl --silent --fail \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \ + http://127.0.0.1:8545 > /dev/null; do + sleep 1 +done + +# Wait for http-server on port 9090 +echo "Waiting for http-server on port 9090..." +until curl --silent --fail http://127.0.0.1:9090 > /dev/null; do + sleep 1 +done + +# Wait for Remix serve:production on port 8080 +echo "Waiting for Remix server on port 8080..." +until curl --silent --fail http://127.0.0.1:8080 > /dev/null; do + sleep 1 +done # grep -IRiL "@disabled" "dist/apps/remix-ide-e2e/src/tests" | grep "\.spec\|\.test" | xargs -I {} basename {} .test.js | grep -E "\b[${2}]" # TESTFILES=$(grep -IRiL "@disabled" "dist/apps/remix-ide-e2e/src/tests" | grep "\.spec\|\.test" | xargs -I {} basename {} .test.js | grep -E "\b[$2]" | circleci tests split --split-by=timings ) diff --git a/apps/remix-ide/ci/browser_test_plugin.sh b/apps/remix-ide/ci/browser_test_plugin.sh index 1f5afbd28cd..e6414cf5d89 100755 --- a/apps/remix-ide/ci/browser_test_plugin.sh +++ b/apps/remix-ide/ci/browser_test_plugin.sh @@ -2,6 +2,27 @@ set -e +# if $1 is chrome +# if [ "$1" == "chrome" ]; then +# # 1) Start ChromeDriver in the background +# /usr/local/bin/chromedriver \ +# --port=9515 \ +# --host=127.0.0.1 \ +# --silent > driver.log 2>&1 & + + +# # Save its PID so you can kill it later if needed +# echo $! > chromedriver.pid + +# # 2) Wait until ChromeDriver is actually listening +# while ! curl -s http://127.0.0.1:9515/status >/dev/null; do +# sleep 0.2 +# done + +# echo "🚀 ChromeDriver is up on 127.0.0.1:9515" + +# fi + BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}} echo "$BUILD_ID" TEST_EXITCODE=0 @@ -11,7 +32,32 @@ npx ganache & npx http-server -p 9090 --cors='*' ./node_modules & yarn run serve:production & -sleep 5 +# Wait for Ganache (default port 8545) via JSON-RPC +echo "Waiting for Ganache on port 8545 (eth_blockNumber)..." +until curl --silent --fail \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \ + http://127.0.0.1:8545 > /dev/null; do + sleep 1 +done + +# Wait for http-server on port 9999 +echo "Waiting for http-server on port 9999..." +until curl --silent --fail http://127.0.0.1:9999 > /dev/null; do + sleep 1 +done + +# Wait for http-server on port 9090 +echo "Waiting for http-server on port 9090..." +until curl --silent --fail http://127.0.0.1:9090 > /dev/null; do + sleep 1 +done + +# Wait for Remix serve:production on port 8080 +echo "Waiting for Remix server on port 8080..." +until curl --silent --fail http://127.0.0.1:8080 > /dev/null; do + sleep 1 +done TESTFILES=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep $1 | sort | circleci tests split ) for TESTFILE in $TESTFILES; do diff --git a/apps/remix-ide/ci/flaky.sh b/apps/remix-ide/ci/flaky.sh index 5373daec0b0..23f2894154a 100755 --- a/apps/remix-ide/ci/flaky.sh +++ b/apps/remix-ide/ci/flaky.sh @@ -20,7 +20,27 @@ TEST_EXITCODE=0 npx ganache & npx http-server -p 9090 --cors='*' ./node_modules & yarn run serve:production & -sleep 5 + +# Wait for Ganache (default port 8545) via JSON-RPC +echo "Waiting for Ganache on port 8545 (eth_blockNumber)..." +until curl --silent --fail \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \ + http://127.0.0.1:8545 > /dev/null; do + sleep 1 +done + +# Wait for http-server on port 9090 +echo "Waiting for http-server on port 9090..." +until curl --silent --fail http://127.0.0.1:9090 > /dev/null; do + sleep 1 +done + +# Wait for Remix serve:production on port 8080 +echo "Waiting for Remix server on port 8080..." +until curl --silent --fail http://127.0.0.1:8080 > /dev/null; do + sleep 1 +done for TESTFILE in $TESTFILES; do npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch-${1}.js $TESTFILE --env=$1 || TEST_EXITCODE=1 diff --git a/apps/remix-ide/ci/metamask.sh b/apps/remix-ide/ci/metamask.sh index f2780e25123..76c2c7985e4 100755 --- a/apps/remix-ide/ci/metamask.sh +++ b/apps/remix-ide/ci/metamask.sh @@ -27,7 +27,7 @@ sleep 5 for TESTFILE in $TESTFILES; do echo "Running metamask test: $TESTFILE" echo "running with env $1" - npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch-chrome.js $TESTFILE --env=$1 || TEST_EXITCODE=1 + METAMASK_TEST=true npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch-chrome.js $TESTFILE --env=$1 || TEST_EXITCODE=1 done echo "$TEST_EXITCODE" diff --git a/apps/remix-ide/src/app/components/popup-panel.tsx b/apps/remix-ide/src/app/components/popup-panel.tsx index 829516adc20..8b1bf8c1188 100644 --- a/apps/remix-ide/src/app/components/popup-panel.tsx +++ b/apps/remix-ide/src/app/components/popup-panel.tsx @@ -86,34 +86,6 @@ export class PopupPanel extends AbstractPanel { } updateComponent(state: popupPanelState, appState: Partial) { - if (!this.hooks) { - try { - const markdown = document.getElementsByClassName('nlux-composer-container') - const button = markdown[0].getElementsByTagName('button')[0] - const textArea = markdown[0].getElementsByTagName('textarea')[0] - // only add event listeners if they are not already added - if (!textArea.dataset.listenerAdded) { - textArea.addEventListener('input', (event) => { - const sanitizedInput = DOMPurify.sanitize(textArea.value) - if (sanitizedInput !== textArea.value) { - textArea.value = sanitizedInput - } - }) - textArea.dataset.listenerAdded = 'true' - } - - if (!button.dataset.listenerAdded) { - button.dataset.listenerAdded = 'true' - button.addEventListener('click', (event) => { - const sanitizedInput = DOMPurify.sanitize(textArea.value) - if (sanitizedInput !== textArea.value) { - textArea.value = sanitizedInput - } - }) - } - this.hooks = true - } catch (error) { this.hooks = false } - } return (
{ server.stdout.on('data', function (data) { console.log(data.toString()) if ( - data.toString().includes("foundryup: use - chisel 1.0.0-stable") + data.toString().includes("use - chisel") ) { console.log('resolving') resolve() diff --git a/libs/remix-ui/remix-ai/src/lib/components/Default.tsx b/libs/remix-ui/remix-ai/src/lib/components/Default.tsx index b18d42bfdd0..020dff530c1 100644 --- a/libs/remix-ui/remix-ai/src/lib/components/Default.tsx +++ b/libs/remix-ui/remix-ai/src/lib/components/Default.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useRef } from 'react' import '../remix-ai.css' import { DefaultModels, GenerationParams, ChatHistory, HandleStreamResponse } from '@remix/remix-ai-core'; import { ConversationStarter, StreamSend, StreamingAdapterObserver, useAiChatApi } from '@nlux/react'; @@ -9,10 +9,55 @@ import './color.css' import '@nlux/themes/unstyled.css'; import copy from 'copy-to-clipboard' +// Using DOMPurify for sanitization +import DOMPurify from 'dompurify'; + +// Function to sanitize user input +const sanitizeInput = (input: string) => DOMPurify.sanitize(input); + export let ChatApi = null export const Default = (props) => { const [is_streaming, setIS_streaming] = useState(false) + const containerRef = useRef(null); + + // use refs to access the DOM elements and remove event listeners when unmounting + useEffect(() => { + if (containerRef.current) { + + const textArea = containerRef.current?.getElementsByClassName('nlux-comp-composer')[0].getElementsByTagName('textarea')[0] + if (!textArea) return; + + const onInput = (e: Event) => { + const sanitizedInput = sanitizeInput((e.target as HTMLTextAreaElement).value); + if (sanitizedInput !== (e.target as HTMLTextAreaElement).value) { + (e.target as HTMLTextAreaElement).value = sanitizedInput; + } + }; + + textArea.addEventListener('input', onInput); + + const sendButton = containerRef.current?.getElementsByClassName('nlux-comp-composer')[0].getElementsByTagName('button')[0]; + const onClick = (e: Event) => { + + if (textArea) { + const sanitized = sanitizeInput(textArea.value); + + if (sanitized !== textArea.value) { + textArea.value = sanitized; + } + } + }; + if (sendButton) { + sendButton.addEventListener('click', onClick); + } + + return () => { + textArea.removeEventListener('input', onInput); + if (sendButton) sendButton.removeEventListener('click', onClick); + }; + } + }, [containerRef]); const HandleCopyToClipboard = () => { const markdown = document.getElementsByClassName('nlux-chatSegments-container') @@ -39,6 +84,7 @@ export const Default = (props) => { prompt: string, observer: StreamingAdapterObserver, ) => { + const cleanPrompt = sanitizeInput(prompt); GenerationParams.stream_result = true setIS_streaming(true) GenerationParams.return_stream_response = GenerationParams.stream_result @@ -47,14 +93,14 @@ export const Default = (props) => { if (await props.plugin.call('remixAI', 'isChatRequestPending')){ response = await props.plugin.call('remixAI', 'ProcessChatRequestBuffer', GenerationParams); } else { - response = await props.plugin.call('remixAI', 'solidity_answer', prompt, GenerationParams); + response = await props.plugin.call('remixAI', 'solidity_answer', cleanPrompt, GenerationParams); } if (GenerationParams.return_stream_response) HandleStreamResponse(response, (text) => {observer.next(text)}, (result) => { observer.next(' ') // Add a space to flush the last message - ChatHistory.pushHistory(prompt, result) + ChatHistory.pushHistory(cleanPrompt, result) observer.complete() setTimeout(() => { setIS_streaming(false) }, 1000) } @@ -81,30 +127,33 @@ export const Default = (props) => { const adapter = useAsStreamAdapter(send, []); return ( - + + + + ); }; \ No newline at end of file