diff --git a/assets/css/browser-solidity.css b/assets/css/browser-solidity.css
index a7f5839845e..b09eb240b6a 100644
--- a/assets/css/browser-solidity.css
+++ b/assets/css/browser-solidity.css
@@ -1,3 +1,6 @@
+html { box-sizing: border-box; }
+*, *:before, *:after { box-sizing: inherit; }
+
body {
padding: 0;
font-size: 12px;
@@ -39,11 +42,11 @@ body {
left: 0;
}
-.files-wrapper {
+#tabs-bar {
position: absolute;
overflow: hidden;
top: 0;
- left: 5em;
+ left: 200px;
right: 3em;
}
@@ -75,8 +78,6 @@ body {
color: #999;
}
-.newFile,
-.uploadFile,
.toggleRHP {
display: block;
float: left;
@@ -124,18 +125,6 @@ body {
display: inline-block;
}
-#input {
- border-top: 3px solid #F4F6FF;
- padding-top: 0.5em;
- font-size: 15px;
- position: absolute;
- top: 2.5em;
- left: 0;
- right: 0;
- bottom: 0;
- min-width: 20vw;
-}
-
#righthand-panel {
position: absolute;
top: 0;
@@ -161,6 +150,7 @@ body {
float: right;
height: 90%;
background-color: white;
+ padding-right: 1%;
}
#header #menu {
@@ -475,7 +465,7 @@ body {
bottom: 0;
cursor: col-resize;
z-index: 999;
- border-right: 3px solid #F4F6FF;
+ border-right: 2px solid #C6CFF7;
}
#editor .ace-tm .ace_gutter,
diff --git a/index.html b/index.html
index d1cca62607e..e882ab0847e 100644
--- a/index.html
+++ b/index.html
@@ -41,15 +41,16 @@
-
-
-
diff --git a/src/app.js b/src/app.js
index 4da7b9c1fab..2fac32971e6 100644
--- a/src/app.js
+++ b/src/app.js
@@ -1,10 +1,11 @@
-/* global alert, confirm, prompt, FileReader, Option, Worker, chrome */
+/* global alert, confirm, prompt, Option, Worker, chrome */
'use strict'
var async = require('async')
var $ = require('jquery')
var base64 = require('js-base64').Base64
var swarmgw = require('swarmgw')
+var csjs = require('csjs-inject')
var QueryParams = require('./app/query-params')
var queryParams = new QueryParams()
@@ -24,6 +25,7 @@ var FormalVerification = require('./app/formalVerification')
var EventManager = require('./lib/eventManager')
var StaticAnalysis = require('./app/staticanalysis/staticAnalysisView')
var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter')
+var FilePanel = require('./app/file-panel')
var examples = require('./app/example-contracts')
@@ -84,12 +86,6 @@ var run = function () {
loadFiles(filesToLoad)
}
- // -------- check file upload capabilities -------
-
- if (!(window.File || window.FileReader || window.FileList || window.Blob)) {
- $('.uploadFile').remove()
- }
-
// ------------------ gist load ----------------
var loadingFromGist = gistHandler.handleLoad(queryParams.get(), function (gistId) {
@@ -159,26 +155,83 @@ var run = function () {
chromeCloudSync()
// ----------------- editor ----------------------
-
var editor = new Editor(document.getElementById('input'))
- // ----------------- tabbed menu -------------------
- $('#options li').click(function (ev) {
- var $el = $(this)
- selectTab($el)
- })
-
- var selectTab = function (el) {
- var match = /[a-z]+View/.exec(el.get(0).className)
- if (!match) return
- var cls = match[0]
- if (!el.hasClass('active')) {
- el.parent().find('li').removeClass('active')
- $('#optionViews').attr('class', '').addClass(cls)
- el.addClass('active')
+ // ---------------- FilePanel --------------------
+ /****************************************************************************
+ @TODO's
+ 1. I would put a virtual file called Summary as the root entry of the treeview, which displays the list of the files with the size in bytes of each
+
+ 2. drag'n'drop to enable to rename files&folders in the file explorer into different sub folders
+
+ 3. I would put a virtual file called `Summary` as the root entry of the treeview, which displays the list of the files with the size in bytes of each.
+
+ 4. add maybe more tape tests
+
+ 5. gist imports + copy to the browser => phase of writing
+
+ 6. add filemanagement from righthand panel to filepanel compoennt (editing/imports/exports, public gist, load from github, create new project, ... setup load and modify files)
+ */
+ // var sources = {
+ // 'test/client/credit.sol': '',
+ // 'src/voting.sol': '',
+ // 'src/leasing.sol': '',
+ // 'src/gmbh/contract.sol': false,
+ // 'src/gmbh/test.sol': false,
+ // 'src/gmbh/company.sol': false,
+ // 'src/gmbh/node_modules/ballot.sol': false,
+ // 'src/ug/finance.sol': false,
+ // 'app/solidity/mode.sol': true,
+ // 'app/ethereum/constitution.sol': true
+ // }
+ // Object.keys(sources).forEach(function (key) { files.set(key, sources[key]) })
+ /****************************************************************************/
+ var css = csjs`
+ .filepanel {
+ display : flex;
+ width : 200px;
+ }
+ `
+ var filepanel = document.querySelector('#filepanel')
+ filepanel.className = css.filepanel
+ var FilePanelAPI = {
+ createName: createNonClashingName,
+ switchToFile: switchToFile
+ }
+ var el = new FilePanel(FilePanelAPI, files)
+ filepanel.appendChild(el)
+ var api = el.api
+
+ api.register('ui', function changeLayout (data) {
+ var value
+ if (data.type === 'minimize') {
+ value = -parseInt(window['filepanel'].style.width)
+ value = (isNaN(value) ? -window['filepanel'].getBoundingClientRect().width : value)
+ window['filepanel'].style.position = 'absolute'
+ window['filepanel'].style.left = (value - 5) + 'px'
+ window['filepanel'].style.width = -value + 'px'
+ window['tabs-bar'].style.left = '45px'
+ } else if (data.type === 'maximize') {
+ value = -parseInt(window['filepanel'].style.left) + 'px'
+ window['filepanel'].style.position = 'static'
+ window['filepanel'].style.width = value
+ window['filepanel'].style.left = ''
+ window['tabs-bar'].style.left = value
+ } else {
+ window['filepanel'].style.width = data.width + 'px'
+ window['tabs-bar'].style.left = data.width + 'px'
}
- self.event.trigger('tabChanged', [cls])
- }
+ })
+ api.register('focus', function (path) {
+ [...window.files.querySelectorAll('.file .name')].forEach(function (span) {
+ if (span.innerText === path) switchToFile(path) // @TODO: scroll into view
+ })
+ })
+ files.event.register('fileRenamed', function (oldName, newName) {
+ [...window.files.querySelectorAll('.file .name')].forEach(function (span) {
+ if (span.innerText === oldName) span.innerText = newName
+ })
+ })
// ------------------ gist publish --------------
@@ -221,39 +274,26 @@ var run = function () {
}).appendTo('body')
})
- // ----------------- file selector-------------
-
- var $filesEl = $('#files')
- var FILE_SCROLL_DELTA = 300
-
- $('.newFile').on('click', function () {
- var newName = createNonClashingName('Untitled')
- if (!files.set(newName, '')) {
- alert('Failed to create file ' + newName)
- } else {
- switchToFile(newName)
- }
+ // ---------------- tabbed menu ------------------
+ $('#options li').click(function (ev) {
+ var $el = $(this)
+ selectTab($el)
})
- // ----------------- file upload -------------
-
- $('.inputFile').on('change', function () {
- var fileList = $('input.inputFile')[0].files
- for (var i = 0; i < fileList.length; i++) {
- var name = fileList[i].name
- if (!files.exists(name) || confirm('The file ' + name + ' already exists! Would you like to overwrite it?')) {
- var fileReader = new FileReader()
- fileReader.onload = function (ev) {
- if (!files.set(name, ev.target.result)) {
- alert('Failed to create file ' + name)
- } else {
- switchToFile(name)
- }
- }
- fileReader.readAsText(fileList[i])
- }
+ var selectTab = function (el) {
+ var match = /[a-z]+View/.exec(el.get(0).className)
+ if (!match) return
+ var cls = match[0]
+ if (!el.hasClass('active')) {
+ el.parent().find('li').removeClass('active')
+ $('#optionViews').attr('class', '').addClass(cls)
+ el.addClass('active')
}
- })
+ self.event.trigger('tabChanged', [cls])
+ }
+
+ var $filesEl = $('#files')
+ var FILE_SCROLL_DELTA = 300
// Switch tab
$filesEl.on('click', '.file:not(.active)', function (ev) {
@@ -325,6 +365,8 @@ var run = function () {
currentFile = file
+ files.event.trigger('fileFocus', [file])
+
if (files.isReadOnly(file)) {
editor.openReadOnly(file, files.get(file))
} else {
@@ -368,7 +410,6 @@ var run = function () {
})
}
- var $filesWrapper = $('.files-wrapper')
var $scrollerRight = $('.scroller-right')
var $scrollerLeft = $('.scroller-left')
@@ -381,12 +422,8 @@ var run = function () {
return itemsWidth
}
- // function widthOfHidden () {
- // return ($filesWrapper.outerWidth() - widthOfList() - getLeftPosi())
- // }
-
function widthOfVisible () {
- return $filesWrapper.outerWidth()
+ return document.querySelector('#editor-container').offsetWidth
}
function getLeftPosi () {
diff --git a/src/app/editor.js b/src/app/editor.js
index e5f4e59e0a0..19475be4d79 100644
--- a/src/app/editor.js
+++ b/src/app/editor.js
@@ -2,13 +2,34 @@
var EventManager = require('../lib/eventManager')
+var csjs = require('csjs-inject')
var ace = require('brace')
var Range = ace.acequire('ace/range').Range
require('../mode-solidity.js')
+var css = csjs`
+ .editor-container {
+ display : flex;
+ position : absolute;
+ top : 2.5em;
+ left : 0;
+ right : 0;
+ bottom : 0;
+ min-width : 20vw;
+ }
+ .ace-editor {
+ top : 4px;
+ border-top : 3px solid transparent;
+ font-size : 15px;
+ width : 100%;
+ }
+`
+document.querySelector('#editor-container').className = css['editor-container']
+
function Editor (editorElement) {
var editor = ace.edit(editorElement)
editorElement.editor = editor // required to access the editor during tests
+ editorElement.className += ' ' + css['ace-editor']
var event = new EventManager()
this.event = event
var sessions = {}
diff --git a/src/app/file-explorer.js b/src/app/file-explorer.js
new file mode 100755
index 00000000000..800afde4009
--- /dev/null
+++ b/src/app/file-explorer.js
@@ -0,0 +1,318 @@
+/* global FileReader, confirm, alert */
+var yo = require('yo-yo')
+var csjs = require('csjs-inject')
+var Treeview = require('ethereum-remix').ui.TreeView
+
+var EventManager = require('../lib/eventManager')
+
+var css = csjs`
+ .fileexplorer {
+ box-sizing : border-box;
+ }
+ .folder,
+ .file {
+ font-size : 14px;
+ }
+ .hasFocus {
+ background-color : #F4F6FF;
+ }
+ .rename {
+ background-color : white;
+ }
+ .remove {
+ align-self : center;
+ padding-left : 10px;
+ }
+ .activeMode {
+ display : flex;
+ justify-content : space-between;
+ margin-right : 10px;
+ padding-right : 19px;
+ }
+ ul {
+ padding : 0;
+ }
+`
+module.exports = fileExplorer
+
+function fileExplorer (appAPI, files) {
+ var fileEvents = files.event
+ var tv = new Treeview({
+ extractData: function (value, tree, key) {
+ var newValue = {}
+ // var isReadOnly = false
+ var isFile = false
+ Object.keys(value).filter(function keep (x) {
+ if (x === '/content') isFile = true
+ // if (x === '/readOnly') isReadOnly = true
+ if (x[0] !== '/') return true
+ }).forEach(function (x) { newValue[x] = value[x] })
+ return {
+ path: (tree || {}).path ? tree.path + '/' + key : key,
+ children: isFile ? undefined
+ : value instanceof Array ? value.map((item, index) => ({
+ key: index, value: item
+ })) : value instanceof Object ? Object.keys(value).map(subkey => ({
+ key: subkey, value: value[subkey]
+ })) : undefined
+ }
+ },
+ formatSelf: function (key, data) {
+ return yo`
`
+ }
+ })
+
+ var deleteButton = yo`
+
+
+
+ `
+
+ fileEvents.register('fileFocus', fileFocus)
+ fileEvents.register('fileRemoved', fileRemoved)
+ fileEvents.register('fileRenamed', fileRenamed)
+ fileEvents.register('fileAdded', fileAdded)
+ fileEvents.register('fileChanged', fileChanged)
+
+ var filepath = null
+ var focusElement = null
+ var textUnderEdit = null
+
+ var element = tv.render(files.listAsTree())
+ element.className = css.fileexplorer
+
+ var api = new EventManager()
+ api.addFile = function addFile (file) {
+ var name = file.name
+ if (!files.exists(name) || confirm('The file ' + name + ' already exists! Would you like to overwrite it?')) {
+ var fileReader = new FileReader()
+ fileReader.onload = function (event) {
+ var success = files.set(name, event.target.result)
+ if (!success) alert('Failed to create file ' + name)
+ else api.trigger('focus', [name])
+ }
+ fileReader.readAsText(file)
+ }
+ }
+
+ function focus (event) {
+ event.cancelBubble = true
+ var li = this
+ if (focusElement === li) return
+ if (focusElement) focusElement.classList.toggle(css.hasFocus)
+ focusElement = li
+ focusElement.classList.toggle(css.hasFocus)
+ var label = getLabelFrom(li)
+ var filepath = label.dataset.path
+ var isFile = label.className.indexOf('file') === 0
+ if (isFile) api.trigger('focus', [filepath])
+ }
+
+ function hover (event) {
+ if (event.type === 'mouseout') {
+ var exitedTo = event.toElement || event.relatedTarget
+ if (this.contains(exitedTo)) return
+ this.style.backgroundColor = ''
+ this.style.paddingRight = '19px'
+ return this.removeChild(deleteButton)
+ }
+ this.style.backgroundColor = '#F4F6FF'
+ this.style.paddingRight = '0px'
+ this.appendChild(deleteButton)
+ }
+
+ function getElement (path) {
+ var label = element.querySelector(`label[data-path="${path}"]`)
+ if (label) return getLiFrom(label)
+ }
+
+ function deletePath (event) {
+ event.cancelBubble = true
+ var span = this
+ var li = span.parentElement.parentElement
+ var label = getLabelFrom(li)
+ var path = label.dataset.path
+ var isFolder = !!~label.className.indexOf('folder')
+ if (confirm(`
+ Do you really want to delete "${path}" ?
+ ${isFolder ? '(and all contained files and folders)' : ''}
+ `)) {
+ li.parentElement.removeChild(li)
+ removeSubtree(files, path)
+ }
+ }
+
+ function editModeOn (event) {
+ var label = this
+ var li = getLiFrom(label)
+ var classes = li.className
+ if (~classes.indexOf('hasFocus') && !label.getAttribute('contenteditable')) {
+ textUnderEdit = label.innerText
+ label.setAttribute('contenteditable', true)
+ label.classList.add(css.rename)
+ label.focus()
+ }
+ }
+
+ function editModeOff (event) {
+ var label = this
+ if (event.type === 'blur' || event.which === 27 || event.which === 13) {
+ var save = textUnderEdit !== label.innerText
+ if (event.which === 13) event.preventDefault()
+ if (save && event.which !== 13) save = confirm('Do you want to rename?')
+ if (save) renameSubtree(label)
+ else label.innerText = textUnderEdit
+ label.removeAttribute('contenteditable')
+ label.classList.remove(css.rename)
+ }
+ }
+
+ function renameSubtree (label, dontcheck) {
+ var oldPath = label.dataset.path
+ var newPath = oldPath
+ newPath = newPath.split('/')
+ newPath[newPath.length - 1] = label.innerText
+ newPath = newPath.join('/')
+ if (!dontcheck) {
+ var allPaths = Object.keys(files.list())
+ for (var i = 0, len = allPaths.length, path, err; i < len; i++) {
+ path = allPaths[i]
+ if (files.IsReadOnly(path)) {
+ err = 'path contains readonly elements'
+ break
+ } else if (path.indexOf(newPath) === 0) {
+ err = 'new path is conflicting with another existing path'
+ break
+ }
+ }
+ }
+ if (err) {
+ alert(`couldn't rename - ${err}`)
+ label.innerText = textUnderEdit
+ } else {
+ textUnderEdit = label.innerText
+ updateAllLabels([getElement(oldPath)], oldPath, newPath)
+ }
+ }
+
+ function updateAllLabels (lis, oldPath, newPath) {
+ lis.forEach(function (li) {
+ var label = getLabelFrom(li)
+ var path = label.dataset.path
+ var newName = path.replace(oldPath, newPath)
+ label.dataset.path = newName
+ var isFile = label.className.indexOf('file') === 0
+ if (isFile) files.rename(path, newName)
+ var ul = li.lastChild
+ if (ul.tagName === 'UL') {
+ updateAllLabels([...ul.children], oldPath, newPath)
+ }
+ })
+ }
+
+ function fileChanged (filepath) { }
+
+ function fileFocus (path) {
+ if (filepath === path) return
+ filepath = path
+ var el = getElement(filepath)
+ expandPathTo(el)
+ setTimeout(function focusNode () { el.click() }, 0)
+ }
+
+ function fileRemoved (filepath) {
+ var li = getElement(filepath)
+ if (li) li.parentElement.removeChild(li)
+ }
+
+ function fileRenamed (oldName, newName) {
+ var li = getElement(oldName)
+ if (li) {
+ oldName = oldName.split('/')
+ newName = newName.split('/')
+ var index = oldName.reduce(function (idx, key, i) {
+ return oldName[i] !== newName[i] ? i : idx
+ }, undefined)
+ var newKey = newName[index]
+ var oldPath = oldName.slice(0, index + 1).join('/')
+ li = getElement(oldPath)
+ var label = getLabelFrom(li)
+ label.innerText = newKey
+ renameSubtree(label, true)
+ }
+ }
+
+ function fileAdded (filepath) {
+ var el = tv.render(files.listAsTree())
+ el.className = css.fileexplorer
+ element.parentElement.replaceChild(el, element)
+ element = el
+ fileFocus(filepath)
+ appAPI.switchToFile(filepath)
+ }
+
+ element.api = api
+ return element
+}
+/******************************************************************************
+ HELPER FUNCTIONS
+******************************************************************************/
+function adaptEnvironment (label, focus, hover) {
+ var li = getLiFrom(label)
+ li.style.position = 'relative'
+ var span = li.firstChild
+ // add focus
+ li.addEventListener('click', focus)
+ // add hover
+ span.classList.add(css.activeMode)
+ span.addEventListener('mouseover', hover)
+ span.addEventListener('mouseout', hover)
+}
+
+function unadaptEnvironment (label, focus, hover) {
+ var li = getLiFrom(label)
+ var span = li.firstChild
+ li.style.position = undefined
+ // remove focus
+ li.removeEventListener('click', focus)
+ // remove hover
+ span.classList.remove(css.activeMode)
+ span.removeEventListener('mouseover', hover)
+ span.removeEventListener('mouseout', hover)
+}
+
+function getLiFrom (label) {
+ return label.parentElement.parentElement.parentElement
+}
+
+function getLabelFrom (li) {
+ return li.children[0].children[1].children[0]
+}
+
+function removeSubtree (files, path) {
+ var allPaths = Object.keys(files.list()) // @TODO: change `files`
+ var removePaths = allPaths.filter(function (p) { return ~p.indexOf(path) })
+ removePaths.forEach(function (path) {
+ [...window.files.querySelectorAll('.file .name')].forEach(function (span) {
+ if (span.innerText === path) {
+ var li = span.parentElement
+ li.parentElement.removeChild(li) // delete tab
+ }
+ })
+ files.remove(path)
+ })
+}
+
+function expandPathTo (li) {
+ while ((li = li.parentElement.parentElement) && li.tagName === 'LI') {
+ var caret = li.firstChild.firstChild
+ if (caret.classList.contains('fa-caret-right')) caret.click() // expand
+ }
+}
diff --git a/src/app/file-panel.js b/src/app/file-panel.js
new file mode 100644
index 00000000000..a296e258045
--- /dev/null
+++ b/src/app/file-panel.js
@@ -0,0 +1,173 @@
+/* global alert */
+var csjs = require('csjs-inject')
+var yo = require('yo-yo')
+
+var EventManager = require('../lib/eventManager')
+var FileExplorer = require('./file-explorer')
+
+module.exports = filepanel
+
+var css = csjs`
+ .container {
+ display : flex;
+ flex-direction : row;
+ width : 100%;
+ box-sizing : border-box;
+ }
+ .fileexplorer {
+ display : flex;
+ flex-direction : column;
+ position : relative;
+ top : -33px;
+ width : 100%;
+ }
+ .menu {
+ display : flex;
+ flex-direction : row;
+ }
+ .newFile {
+ padding : 10px;
+ }
+ .uploadFile {
+ padding : 10px;
+ }
+ .toggleLHP {
+ display : flex;
+ justify-content : flex-end;
+ padding : 10px;
+ width : 100%;
+ font-weight : bold;
+ cursor : pointer;
+ color : black;
+ }
+ .isVisible {
+ position : absolute;
+ left : 35px;
+ }
+ .isHidden {
+ position : absolute;
+ height : 99%
+ left : -101%;
+ }
+ .treeview {
+ height : 100%;
+ background-color : white;
+ }
+ .dragbar {
+ position : relative;
+ top : 6px;
+ cursor : col-resize;
+ z-index : 999;
+ border-right : 2px solid #C6CFF7;
+ }
+ .ghostbar {
+ width : 3px;
+ background-color : #C6CFF7;
+ opacity : 0.5;
+ position : absolute;
+ cursor : col-resize;
+ z-index : 9999;
+ top : 0;
+ bottom : 0;
+ }
+`
+
+var limit = 60
+var canUpload = window.File || window.FileReader || window.FileList || window.Blob
+var ghostbar = yo`
`
+
+function filepanel (appAPI, files) {
+ var fileExplorer = new FileExplorer(appAPI, files)
+ var dragbar = yo`
`
+
+ function template () {
+ return yo`
+
+ `
+ }
+
+ var api = new EventManager()
+ var element = template()
+ element.api = api
+ fileExplorer.api.register('focus', function (path) {
+ api.trigger('focus', [path])
+ })
+
+ return element
+
+ function toggle (event) {
+ var isHidden = element.classList.toggle(css.isHidden)
+ this.classList.toggle(css.isVisible)
+ this.children[0].classList.toggle('fa-angle-double-right')
+ this.children[0].classList.toggle('fa-angle-double-left')
+ api.trigger('ui', [{ type: isHidden ? 'minimize' : 'maximize' }])
+ }
+
+ function uploadFile (event) {
+ ;[...this.files].forEach(fileExplorer.api.addFile)
+ }
+
+ function mousedown (event) {
+ event.preventDefault()
+ if (event.which === 1) {
+ moveGhostbar(event)
+ document.body.appendChild(ghostbar)
+ document.addEventListener('mousemove', moveGhostbar)
+ document.addEventListener('mouseup', removeGhostbar)
+ document.addEventListener('keydown', cancelGhostbar)
+ }
+ }
+ function cancelGhostbar (event) {
+ if (event.keyCode === 27) {
+ document.body.removeChild(ghostbar)
+ document.removeEventListener('mousemove', moveGhostbar)
+ document.removeEventListener('mouseup', removeGhostbar)
+ document.removeEventListener('keydown', cancelGhostbar)
+ }
+ }
+ function moveGhostbar (event) {
+ var rhp = window['righthand-panel'].offsetLeft
+ var newpos = (event.pageX < limit) ? limit : event.pageX
+ newpos = (newpos < (rhp - limit)) ? newpos : (rhp - limit)
+ ghostbar.style.left = newpos + 'px'
+ }
+
+ function removeGhostbar (event) {
+ document.body.removeChild(ghostbar)
+ document.removeEventListener('mousemove', moveGhostbar)
+ document.removeEventListener('mouseup', removeGhostbar)
+ document.removeEventListener('keydown', cancelGhostbar)
+ var width = (event.pageX < limit) ? limit : event.pageX
+ element.style.width = width + 'px'
+ api.trigger('ui', [{ type: 'resize', width: width }])
+ }
+
+ function createNewFile () {
+ var newName = appAPI.createName('Untitled')
+ if (!files.set(newName, '')) {
+ alert('Failed to create file ' + newName)
+ } else {
+ appAPI.switchToFile(newName)
+ }
+ }
+}
diff --git a/test-browser/tests/fileExplorer.js b/test-browser/tests/fileExplorer.js
new file mode 100644
index 00000000000..c530b9a4226
--- /dev/null
+++ b/test-browser/tests/fileExplorer.js
@@ -0,0 +1,40 @@
+'use strict'
+
+var examples = require('../../src/app/example-contracts')
+var init = require('../helpers/init')
+var sauce = require('./sauce')
+
+var sources = {
+ 'sources': {
+ 'ballot.sol': examples.ballot.content,
+ 'test/client/credit.sol': '',
+ 'src/voting.sol': '',
+ 'src/leasing.sol': '',
+ 'src/gmbh/contract.sol': false,
+ 'src/gmbh/test.sol': false,
+ 'src/gmbh/company.sol': false,
+ 'src/gmbh/node_modules/ballot.sol': false,
+ 'src/ug/finance.sol': false,
+ 'app/solidity/mode.sol': true,
+ 'app/ethereum/constitution.sol': true
+ }
+}
+
+module.exports = {
+ before: function (browser, done) {
+ init(browser, done)
+ },
+ '@sources': function () {
+ return sources
+ },
+ 'FileExplorer': function (browser) {
+ runTests(browser)
+ },
+ tearDown: sauce
+}
+
+function runTests (browser, testData) {
+ browser
+ .waitForElementPresent('#filepanel ul li', 10000, true, function () {})
+ .end()
+}