Merge branch 'master' into rmflaxyremixdtest

pull/5370/head
bunsenstraat 3 years ago committed by GitHub
commit 71de42c337
  1. 101
      .circleci/config.yml
  2. 44
      apps/remix-ide-e2e/src/buildGroupTests.js
  3. 19
      apps/remix-ide-e2e/src/commands/getBrowserLogs.ts
  4. 34
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  5. 1
      apps/remix-ide-e2e/src/types/index.d.ts
  6. 37
      apps/remix-ide/ci/flaky.sh
  7. 2
      apps/remix-ide/src/app/files/fileManager.ts
  8. 14
      apps/remix-ide/src/app/panels/tab-proxy.js

@ -3,6 +3,10 @@
# Check https://circleci.com/docs/2.0/language-javascript/ for more details # Check https://circleci.com/docs/2.0/language-javascript/ for more details
# #
version: 2.1 version: 2.1
parameters:
run_flaky_tests:
type: boolean
default: false
orbs: orbs:
browser-tools: circleci/browser-tools@1.2.3 browser-tools: circleci/browser-tools@1.2.3
jobs: jobs:
@ -135,6 +139,49 @@ jobs:
- store_artifacts: - store_artifacts:
path: ./reports/screenshots path: ./reports/screenshots
flaky-chrome:
docker:
# specify the version you desire here
- image: cimg/node:14.17.6-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
working_directory: ~/remix-project
parallelism: 80
steps:
- browser-tools/install-chrome
- browser-tools/install-chromedriver
- run:
command: |
google-chrome --version
chromedriver --version
java -jar /usr/local/bin/selenium.jar --version
name: Check install
- checkout
- attach_workspace:
at: .
- run: unzip ./persist/dist.zip
- restore_cache:
keys:
- v1-deps-{{ checksum "package-lock.json" }}
- run: npm install
- run:
name: Start Selenium
command: java -jar /usr/local/bin/selenium.jar
background: true
- run: ./apps/remix-ide/ci/flaky.sh chrome
- store_test_results:
path: ./reports/tests
- store_artifacts:
path: ./reports/screenshots
remix-ide-firefox: remix-ide-firefox:
docker: docker:
# specify the version you desire here # specify the version you desire here
@ -177,6 +224,48 @@ jobs:
path: ./reports/tests path: ./reports/tests
- store_artifacts: - store_artifacts:
path: ./reports/screenshots path: ./reports/screenshots
flaky-firefox:
docker:
# specify the version you desire here
- image: cimg/node:14.17.6-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
working_directory: ~/remix-project
parallelism: 80
steps:
- browser-tools/install-firefox
- browser-tools/install-geckodriver
- run:
command: |
firefox --version
geckodriver --version
java -jar /usr/local/bin/selenium.jar --version
name: Check install
- checkout
- attach_workspace:
at: .
- run: unzip ./persist/dist.zip
- restore_cache:
keys:
- v1-deps-{{ checksum "package-lock.json" }}
- run: npm install
- run:
name: Start Selenium
command: java -jar /usr/local/bin/selenium.jar
background: true
- run: ./apps/remix-ide/ci/flaky.sh firefox
- store_test_results:
path: ./reports/tests
- store_artifacts:
path: ./reports/screenshots
remix-ide-plugin-api: remix-ide-plugin-api:
docker: docker:
@ -306,10 +395,20 @@ jobs:
if [ "${CIRCLE_BRANCH}" == "remix_beta" ]; then if [ "${CIRCLE_BRANCH}" == "remix_beta" ]; then
./apps/remix-ide/ci/deploy_from_travis_remix-beta.sh; ./apps/remix-ide/ci/deploy_from_travis_remix-beta.sh;
fi fi
workflows: workflows:
version: 2 version: 2
run_flaky_tests:
when: << pipeline.parameters.run_flaky_tests >>
jobs:
- build
- flaky-chrome:
requires:
- build
- flaky-firefox:
requires:
- build
build_all: build_all:
unless: << pipeline.parameters.run_flaky_tests >>
jobs: jobs:
- build - build
- lint: - lint:

@ -15,19 +15,41 @@ fs.readdirSync(testFolder).forEach(file => {
if (!file.includes('group')) { if (!file.includes('group')) {
const content = fs.readFileSync(testFolder + file, 'utf8') const content = fs.readFileSync(testFolder + file, 'utf8')
const matches = content.match(/group\d+/g) const matches = content.match(/group\d+/g)
if (matches) { createFlakyTestFiles(file, content)
const unique = matches.filter(onlyUnique) createFiles(file, matches)
unique.map((group) => {
const rewrite = source.replace('#groupname', group).replace('#file', file.replace('.ts', ''))
const extension = file.split('.')
extension.shift()
const filename = `${testFolder}${file.split('.').shift()}_${group}.${extension.join('.')}`
fs.writeFileSync(filename, rewrite)
})
}
} }
}) })
function onlyUnique (value, index, self) { function createFiles(file, matches, flaky = false) {
if (matches) {
const unique = matches.filter(onlyUnique)
unique.map((group) => {
const rewrite = source.replace('#groupname', group).replace('#file', file.replace('.ts', ''))
const extension = file.split('.')
extension.shift()
let filename
if (!flaky) {
filename = `${testFolder}${file.split('.').shift()}_${group}.${extension.join('.')}`
} else {
filename = `${testFolder}${file.split('.').shift()}_${group}.flaky.ts`
}
fs.writeFileSync(filename, rewrite)
})
}
}
function onlyUnique(value, index, self) {
return self.indexOf(value) === index return self.indexOf(value) === index
} }
function createFlakyTestFiles(file, text) {
const lines = text.split('\n')
lines.forEach((line, index) => {
// if line contains #flaky
if (line.includes('#flaky')) {
const matches = line.match(/group\d+/g)
const unique = matches.filter(onlyUnique)
createFiles(file, matches, true)
}
})
}

@ -0,0 +1,19 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class GetBrowserLogs extends EventEmitter {
command(this: NightwatchBrowser): NightwatchBrowser {
this.api.getLog('browser', function (logs) {
if (logs && Array.isArray(logs)) {
logs.forEach(function (log) {
console.log(log)
}
)
}
}).perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = GetBrowserLogs

@ -4,6 +4,7 @@ import init from '../helpers/init'
import sauce from './sauce' import sauce from './sauce'
module.exports = { module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080?activate=solidity,udapp&call=fileManager//open//contracts/3_Ballot.sol&deactivate=home', false) init(browser, done, 'http://127.0.0.1:8080?activate=solidity,udapp&call=fileManager//open//contracts/3_Ballot.sol&deactivate=home', false)
}, },
@ -15,7 +16,7 @@ module.exports = {
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
}, },
'Editor should be focused on the 3_Ballot.sol': function (browser: NightwatchBrowser) { 'Editor should be focused on the 3_Ballot.sol #group1': function (browser: NightwatchBrowser) {
browser browser
.pause(5000) .pause(5000)
.refresh() .refresh()
@ -25,21 +26,22 @@ module.exports = {
}) })
}, },
'Home page should be deactivated': function (browser: NightwatchBrowser) { 'Home page should be deactivated #group1': function (browser: NightwatchBrowser) {
browser browser
.waitForElementNotPresent('[data-id="landingPageHomeContainer"]') .waitForElementNotPresent('[data-id="landingPageHomeContainer"]')
}, },
'Should create two workspace and switch to the first one': function (browser: NightwatchBrowser) { 'Should create two workspace and switch to the first one #group1': function (browser: NightwatchBrowser) {
browser browser
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.click('*[data-id="workspaceCreate"]') // create workspace_name .click('*[data-id="workspaceCreate"]') // create workspace_name
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span')
// eslint-disable-next-line dot-notation .click('*[data-id="modalDialogCustomPromptTextCreate"]')
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name' }) .clearValue('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name')
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
.pause(1000) .pause(1000)
.addFile('test.sol', { content: 'test' }) .addFile('test.sol', { content: 'test' })
@ -47,10 +49,11 @@ module.exports = {
.click('*[data-id="workspaceCreate"]') // create workspace_name_1 .click('*[data-id="workspaceCreate"]') // create workspace_name_1
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span')
// eslint-disable-next-line dot-notation .click('*[data-id="modalDialogCustomPromptTextCreate"]')
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_name_1' }) .clearValue('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name_1')
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
.pause(2000) .pause(2000)
.waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.sol"]') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.sol"]')
@ -59,15 +62,16 @@ module.exports = {
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
}, },
'Should rename a workspace': function (browser: NightwatchBrowser) { 'Should rename a workspace #group1': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspaceRename"]') // rename workspace_name .click('*[data-id="workspaceRename"]') // rename workspace_name
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextRename"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextRename"]')
// eslint-disable-next-line dot-notation .click('*[data-id="modalDialogCustomPromptTextRename"]')
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextRename"]')['value'] = 'workspace_name_renamed' }) .clearValue('*[data-id="modalDialogCustomPromptTextRename"]')
.setValue('*[data-id="modalDialogCustomPromptTextRename"]', 'workspace_name_renamed')
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.waitForElementPresent('*[data-id="workspacesSelect"] option[value="workspace_name_1"]') .waitForElementPresent('*[data-id="workspacesSelect"] option[value="workspace_name_1"]')
.click('*[data-id="workspacesSelect"] option[value="workspace_name_1"]') .click('*[data-id="workspacesSelect"] option[value="workspace_name_1"]')
.pause(2000) .pause(2000)
@ -78,12 +82,12 @@ module.exports = {
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.sol"]')
}, },
'Should delete a workspace': function (browser: NightwatchBrowser) { 'Should delete a workspace #group1': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspacesSelect"] option[value="workspace_name_1"]') .click('*[data-id="workspacesSelect"] option[value="workspace_name_1"]')
.click('*[data-id="workspaceDelete"]') // delete workspace_name_1 .click('*[data-id="workspaceDelete"]') // delete workspace_name_1
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.waitForElementNotPresent('*[data-id="workspacesSelect"] option[value="workspace_name_1"]') .waitForElementNotPresent('*[data-id="workspacesSelect"] option[value="workspace_name_1"]')
.end() .end()
}, },

@ -61,6 +61,7 @@ declare module 'nightwatch' {
acceptAndRemember (this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser acceptAndRemember (this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser
clearConsole (this: NightwatchBrowser): NightwatchBrowser clearConsole (this: NightwatchBrowser): NightwatchBrowser
clearTransactions (this: NightwatchBrowser): NightwatchBrowser clearTransactions (this: NightwatchBrowser): NightwatchBrowser
getBrowserLogs (this: NightwatchBrowser): NightwatchBrowser
currentSelectedFileIs (name: string): NightwatchBrowser currentSelectedFileIs (name: string): NightwatchBrowser
} }

@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -e
npm run build:e2e
TESTFILES=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep "\.flaky" | sort )
# count test files
fileCount=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep "\.flaky" | wc -l )
# if fileCount is 0
if [ $fileCount -eq 0 ]
then
echo "No flaky tests found"
exit 0
fi
BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}}
echo "$BUILD_ID"
TEST_EXITCODE=0
npm run ganache-cli &
npm run serve:production &
echo 'sharing folder: ' $PWD '/apps/remix-ide/contracts' &
npm run remixd &
npx nx serve remix-ide-e2e-src-local-plugin &
sleep 5
for TESTFILE in $TESTFILES; do
npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js $TESTFILE --env=$1 || TEST_EXITCODE=1
done
echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ]
then
exit 1
fi

@ -600,7 +600,7 @@ class FileManager extends Plugin {
// TODO: Only keep `this.emit` (issue#2210) // TODO: Only keep `this.emit` (issue#2210)
this.emit('fileRemoved', path) this.emit('fileRemoved', path)
this.events.emit('fileRemoved', path) this.events.emit('fileRemoved', path)
this.openFile() this.openFile(this._deps.config.get('currentFile'))
} }
async unselectCurrentFile() { async unselectCurrentFile() {

@ -41,7 +41,13 @@ export class TabProxy extends Plugin {
if (this.fileManager.mode === 'browser') { if (this.fileManager.mode === 'browser') {
name = name.startsWith(workspace + '/') ? name : workspace + '/' + name name = name.startsWith(workspace + '/') ? name : workspace + '/' + name
this.removeTab(name) // If deleted file is not current file and not an active tab in editor,
// ensure current file is active in the editor
if (this.fileManager.currentFile() && name !== this.fileManager.currentFile()) {
const currentFile = this.fileManager.currentFile()
const currentFileTabPath = currentFile.startsWith(workspace + '/') ? currentFile : workspace + '/' + currentFile
this.removeTab(name, { name: currentFileTabPath })
} else this.removeTab(name)
} else { } else {
name = name.startsWith(this.fileManager.mode + '/') ? name : this.fileManager.mode + '/' + name name = name.startsWith(this.fileManager.mode + '/') ? name : this.fileManager.mode + '/' + name
this.removeTab(name) this.removeTab(name)
@ -261,11 +267,11 @@ export class TabProxy extends Plugin {
this._handlers[name] = { switchTo, close } this._handlers[name] = { switchTo, close }
} }
removeTab (name) { removeTab (name, currentFileTab) {
delete this._handlers[name] delete this._handlers[name]
let previous = null let previous = currentFileTab
this.loadedTabs = this.loadedTabs.filter((tab, index) => { this.loadedTabs = this.loadedTabs.filter((tab, index) => {
if (tab.name === name) previous = this.loadedTabs[index - 1] if (!previous && tab.name === name) previous = this.loadedTabs[index - 1]
return tab.name !== name return tab.name !== name
}) })
this.renderComponent() this.renderComponent()

Loading…
Cancel
Save