pull/5370/head
filip mertens 3 years ago
parent 260aa6c94e
commit 3ba601d359
  1. 73
      apps/remix-ide-e2e/src/tests/migrateFileSystem.test.ts
  2. 11
      apps/remix-ide/src/app/components/preload.tsx
  3. 3
      apps/remix-ide/src/app/files/fileSystem.ts
  4. 2
      apps/remix-ide/src/app/files/filesystems/localStorage.ts
  5. 1
      apps/remix-ide/src/assets/js/lightning-fs.min.js
  6. 139
      apps/remix-ide/src/assets/js/migrate.js
  7. 1
      libs/remix-ui/terminal/src/lib/terminalWelcome.tsx

@ -1,25 +1,69 @@
'use strict' 'use strict'
import { NightwatchBrowser } from 'nightwatch' import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
module.exports = { module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) { '@disabled': true,
init(browser, done, 'http://127.0.0.1:8080?e2e_testmigration=true', false) 'Should load the testmigration url #group1': function (browser: NightwatchBrowser) {
browser.url('http://127.0.0.1:8080?e2e_testmigration=true')
.pause(6000)
.switchBrowserTab(0)
.maximizeWindow()
.waitForElementVisible('*[data-id="skipbackup-btn"]', 5000)
.click('*[data-id="skipbackup-btn"]')
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]')
}, },
'Should have README file with TEST README as content': function (browser: NightwatchBrowser) { 'should have indexedDB storage in terminal #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000) browser.assert.containsText('*[data-id="terminalJournal"]', 'indexedDB')
},
'Should fallback to localstorage with default data #group2': function (browser: NightwatchBrowser) {
browser.url('http://127.0.0.1:8080?e2e_testmigration_fallback=true')
.pause(6000)
.switchBrowserTab(0)
.maximizeWindow()
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]')
.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.openFile('README.txt')
.getEditorValue((content) => {
browser.assert.ok(content.includes('Output from script will appear in remix terminal.'))
})
.click('*[data-id="treeViewLitreeViewItemcontracts"]')
.openFile('contracts/1_Storage.sol')
.getEditorValue((content) => {
browser.assert.ok(content.includes('function retrieve() public view returns (uint256){'))
})
},
'Should load the testmigration url with local storage enablbed #group3': function (browser: NightwatchBrowser) {
browser.url('http://127.0.0.1:8080?e2e_testmigration=true&e2e_testmigration_fallback=true')
.pause(6000)
.switchBrowserTab(0)
.maximizeWindow()
.waitForElementVisible('*[data-id="skipbackup-btn"]', 5000)
.click('*[data-id="skipbackup-btn"]')
.waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]')
},
'should have localstorage storage in terminal #group2 #group3': function (browser: NightwatchBrowser) {
browser.assert.containsText('*[data-id="terminalJournal"]', 'localstorage')
},
'Should have README file with TEST README as content #group1 #group3': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]') .waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.openFile('TEST_README.txt') .openFile('TEST_README.txt')
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.equal(content, 'TEST README') browser.assert.equal(content, 'TEST README')
}) })
}, },
'Should have a workspace_test': function (browser: NightwatchBrowser) { 'Should have a workspace_test #group1 #group3': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000) browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.click('*[data-id="workspacesSelect"] option[value="workspace_test"]') .click('*[data-id="workspacesSelect"] option[value="workspace_test"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest_contracts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtest_contracts"]')
}, },
'Should have a sol file with test data': function (browser: NightwatchBrowser) { 'Should have a sol file with test data #group1 #group3': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000) browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.click('*[data-id="treeViewLitreeViewItemtest_contracts"]') .click('*[data-id="treeViewLitreeViewItemtest_contracts"]')
.openFile('test_contracts/1_Storage.sol') .openFile('test_contracts/1_Storage.sol')
@ -27,7 +71,7 @@ module.exports = {
browser.assert.equal(content, 'testing') browser.assert.equal(content, 'testing')
}) })
}, },
'Should have a artifacts file with JSON test data': function (browser: NightwatchBrowser) { 'Should have a artifacts file with JSON test data #group1 #group3': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000) browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.click('*[data-id="treeViewLitreeViewItemtest_contracts/artifacts"]') .click('*[data-id="treeViewLitreeViewItemtest_contracts/artifacts"]')
.openFile('test_contracts/artifacts/Storage_metadata.json') .openFile('test_contracts/artifacts/Storage_metadata.json')
@ -35,5 +79,16 @@ module.exports = {
const metadata = JSON.parse(content) const metadata = JSON.parse(content)
browser.assert.equal(metadata.test, 'data') browser.assert.equal(metadata.test, 'data')
}) })
} },
'Should have a empty workspace #group1 #group3': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000)
.click('*[data-id="workspacesSelect"] option[value="emptyspace"]')
},
'Should load with all storage blocked #group4': function (browser: NightwatchBrowser) {
browser.url('http://127.0.0.1:8080?e2e_testblock_storage=true')
.pause(6000)
.switchBrowserTab(0)
.maximizeWindow()
.assert.containsText('.alert-warning', 'Your browser does not support')
},
} }

@ -35,19 +35,24 @@ export const Preload = () => {
} }
const downloadBackup = async () => { const downloadBackup = async () => {
setShowDownloader(false)
const fsUtility = new fileSystemUtility() const fsUtility = new fileSystemUtility()
await fsUtility.downloadBackup(remixFileSystems.current.fileSystems['localstorage']) await fsUtility.downloadBackup(remixFileSystems.current.fileSystems['localstorage'])
await migrateAndLoad() await migrateAndLoad()
} }
const migrateAndLoad = async () => { const migrateAndLoad = async () => {
setShowDownloader(false)
const fsUtility = new fileSystemUtility() const fsUtility = new fileSystemUtility()
await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current) await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current)
await setFileSystems() await setFileSystems()
} }
const setFileSystems = async() => { const setFileSystems = async() => {
const fsLoaded = await remixFileSystems.current.setFileSystem([remixIndexedDB.current, localStorageFileSystem.current]) // url parameters to e2e test the fallbacks and error warnings
const testmigrationFallback = window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:'
const testBlockStorage = window.location.hash.includes('e2e_testblock_storage=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:'
const fsLoaded = await remixFileSystems.current.setFileSystem([(testmigrationFallback || testBlockStorage)? null: remixIndexedDB.current, testBlockStorage? null:localStorageFileSystem.current])
if (fsLoaded) { if (fsLoaded) {
console.log(fsLoaded.name + ' activated') console.log(fsLoaded.name + ' activated')
loadAppComponent() loadAppComponent()
@ -102,8 +107,8 @@ export const Preload = () => {
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work. This app will be updated now. Please download a backup of your files now to make sure you don't lose your work.
<br></br> <br></br>
You don't need to do anything else, your files will be available when the app loads. You don't need to do anything else, your files will be available when the app loads.
<div onClick={async () => { await downloadBackup() }} className='btn btn-primary mt-1'>download backup</div> <div onClick={async () => { await downloadBackup() }} data-id='downloadbackup-btn' className='btn btn-primary mt-1'>download backup</div>
<div onClick={async () => { await migrateAndLoad() }} className='btn btn-primary mt-1'>skip backup</div> <div onClick={async () => { await migrateAndLoad() }} data-id='skipbackup-btn' className='btn btn-primary mt-1'>skip backup</div>
</div> : null} </div> : null}
{(supported && !error && !showDownloader) ? {(supported && !error && !showDownloader) ?
<div> <div>

@ -43,6 +43,7 @@ export class fileSystem {
const w = (window as any) const w = (window as any)
if (!this.loaded) return false if (!this.loaded) return false
w.remixFileSystem = this.fs w.remixFileSystem = this.fs
w.remixFileSystem.name = this.name
w.remixFileSystemCallback = this.fsCallBack w.remixFileSystemCallback = this.fsCallBack
return true return true
} }
@ -72,7 +73,7 @@ export class fileSystems {
*/ */
setFileSystem = async (filesystems?: fileSystem[]): Promise<fileSystem> => { setFileSystem = async (filesystems?: fileSystem[]): Promise<fileSystem> => {
for (const fs of filesystems) { for (const fs of filesystems) {
if (this.fileSystems[fs.name]) { if (fs && this.fileSystems[fs.name]) {
const result = await this.fileSystems[fs.name].set() const result = await this.fileSystems[fs.name].set()
if (result) return this.fileSystems[fs.name] if (result) return this.fileSystems[fs.name]
} }

@ -16,7 +16,7 @@ export class localStorageFS extends fileSystem {
fs: 'LocalStorage' fs: 'LocalStorage'
}, async function (e) { }, async function (e) {
if (e) { if (e) {
console.log('BROWSEFS Error: ' + e) console.log('BrowserFS Error: ' + e)
reject(e) reject(e)
} else { } else {
me.fs = { ...window.require('fs') } me.fs = { ...window.require('fs') }

File diff suppressed because one or more lines are too long

@ -1,139 +0,0 @@
// eslint-disable-next-line no-unused-vars
async function migrateFilesFromLocalStorage (cb) {
let testmigration = false // migration loads test data into localstorage with browserfs
// indexeddb will be empty by this point, so there is no danger but do a check for the origin to load test data so it runs only locally
testmigration = window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:'
// eslint-disable-next-line no-undef
BrowserFS.install(window)
// eslint-disable-next-line no-undef
BrowserFS.configure({
fs: 'LocalStorage'
}, async function (e) {
if (e) console.log(e)
const browserFS = window.require('fs')
/**
* copy the folder recursively (internal use)
* @param {string} path is the folder to be copied over
* @param {Function} visitFile is a function called for each visited files
* @param {Function} visitFolder is a function called for each visited folders
*/
async function _copyFolderToJsonInternal (path, visitFile, visitFolder, fs) {
visitFile = visitFile || (() => { })
visitFolder = visitFolder || (() => { })
return new Promise((resolve, reject) => {
const json = {}
if (fs.existsSync(path)) {
try {
const items = fs.readdirSync(path)
visitFolder({ path })
if (items.length !== 0) {
items.forEach(async (item, index) => {
const file = {}
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}`
if (fs.statSync(curPath).isDirectory()) {
file.children = await _copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs)
} else {
file.content = fs.readFileSync(curPath, 'utf8')
visitFile({ path: curPath, content: file.content })
}
json[curPath] = file
})
}
} catch (e) {
console.log(e)
return reject(e)
}
}
return resolve(json)
})
}
/**
* copy the folder recursively
* @param {string} path is the folder to be copied over
* @param {Function} visitFile is a function called for each visited files
* @param {Function} visitFolder is a function called for each visited folders
*/
async function copyFolderToJson (path, visitFile, visitFolder, fs) {
visitFile = visitFile || (() => { })
visitFolder = visitFolder || (() => { })
return _copyFolderToJsonInternal(path, visitFile, visitFolder, fs)
}
const populateWorkspace = async (json, fs) => {
for (const item in json) {
const isFolder = json[item].content === undefined
if (isFolder) {
await createDir(item, fs)
await populateWorkspace(json[item].children, fs)
} else {
try {
await fs.writeFile(item, json[item].content, 'utf8')
} catch (error) {
console.log(error)
}
}
}
}
const createDir = async (path, fs) => {
const paths = path.split('/')
if (paths.length && paths[0] === '') paths.shift()
let currentCheck = ''
for (const value of paths) {
currentCheck = currentCheck + (currentCheck ? '/' : '') + value
if (!await fs.exists(currentCheck)) {
try {
await fs.mkdir(currentCheck)
} catch (error) {
console.log(error)
}
}
}
}
//
if (testmigration) await populateWorkspace(testData, browserFS)
const files = await copyFolderToJson('/', null, null, browserFS)
await populateWorkspace(files, window.remixFileSystem)
// eslint-disable-next-line no-undef
if (cb) cb()
})
}
/* eslint-disable no-template-curly-in-string */
const testData = {
'.workspaces': {
children: {
'.workspaces/default_workspace': {
children: {
'.workspaces/default_workspace/README.txt': {
content: 'TEST README'
}
}
},
'.workspaces/workspace_test': {
children: {
'.workspaces/workspace_test/TEST_README.txt': {
content: 'TEST README'
},
'.workspaces/workspace_test/test_contracts': {
children: {
'.workspaces/workspace_test/test_contracts/1_Storage.sol': {
content: 'testing'
},
'.workspaces/workspace_test/test_contracts/artifacts': {
children: {
'.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': {
content: '{ "test": "data" }'
}
}
}
}
}
}
}
}
}
}

@ -4,6 +4,7 @@ const TerminalWelcomeMessage = ({ packageJson }) => {
return ( return (
<div className="remix_ui_terminal_block px-4 " data-id="block_null"> <div className="remix_ui_terminal_block px-4 " data-id="block_null">
<div className="remix_ui_terminal_welcome"> Welcome to Remix {packageJson} </div><br /> <div className="remix_ui_terminal_welcome"> Welcome to Remix {packageJson} </div><br />
<div className="">Your files are stored in {(window as any).remixFileSystem.name} </div><br />
<div>You can use this terminal to: </div> <div>You can use this terminal to: </div>
<ul className='ml-0 mr-4'> <ul className='ml-0 mr-4'>
<li>Check transactions details and start debugging.</li> <li>Check transactions details and start debugging.</li>

Loading…
Cancel
Save