commit
06512f3a2a
@ -0,0 +1,242 @@ |
||||
import { Octokit } from 'octokit' |
||||
import * as fs from 'fs' |
||||
import * as path from 'path' |
||||
import YAML from 'yaml' |
||||
import crypto from 'crypto' |
||||
|
||||
const owner = 'remix-project-org' |
||||
let repo = 'remix-desktop' |
||||
const headers = { |
||||
'X-GitHub-Api-Version': '2022-11-28', |
||||
} |
||||
|
||||
const version = getVersionFromPackageJson() |
||||
let channel = 'latest' |
||||
|
||||
if (version.includes('beta')) { |
||||
channel = 'beta' |
||||
} |
||||
if (version.includes('alpha')) { |
||||
channel = 'alpha' |
||||
} |
||||
if (version.includes('insiders')) { |
||||
channel = 'insiders' |
||||
} |
||||
|
||||
if (channel !== 'latest') repo = `remix-desktop-${channel}` |
||||
|
||||
const octokit = new Octokit({ |
||||
auth: process.env.GH_TOKEN_DESKTOP_PUBLISH, |
||||
}) |
||||
|
||||
async function getAllReleases() { |
||||
const releases = await octokit.request('GET /repos/{owner}/{repo}/releases', { |
||||
owner: owner, |
||||
repo: repo, |
||||
headers: headers, |
||||
}) |
||||
return releases.data |
||||
} |
||||
|
||||
async function uploadReleaseAsset(release, name, file) { |
||||
const upload_url = release.upload_url |
||||
console.log(`Uploading ${name} to ${upload_url}`) |
||||
if (fs.existsSync(file)) { |
||||
octokit.request({ |
||||
method: "POST", |
||||
url: upload_url, |
||||
headers: { |
||||
"content-type": "text/plain", |
||||
}, |
||||
data: fs.readFileSync(file), |
||||
name, |
||||
label: name |
||||
}); |
||||
} else { |
||||
console.log(`File ${file} does not exist. Skipping...`) |
||||
} |
||||
} |
||||
|
||||
function getVersionFromPackageJson() { |
||||
// ignore ts error
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const packageJson = require(__dirname + '/../../../apps/remixdesktop/package.json') |
||||
return packageJson.version |
||||
} |
||||
|
||||
async function readReleaseFilesFromLocalDirectory() { |
||||
const directoryPath = path.join(__dirname, '../../../release') |
||||
const files = fs.readdirSync(directoryPath) |
||||
return files |
||||
} |
||||
|
||||
async function removeAsset(asset) { |
||||
await octokit.request('DELETE /repos/{owner}/{repo}/releases/assets/{asset_id}', { |
||||
owner: owner, |
||||
repo: repo, |
||||
asset_id: asset.id, |
||||
headers: headers, |
||||
}) |
||||
} |
||||
|
||||
async function hashFile(file): Promise<string> { |
||||
return new Promise((resolve, reject) => { |
||||
const hash = crypto.createHash('sha512').setEncoding('base64'); |
||||
// hash.on('error', reject).setEncoding(encoding);
|
||||
fs.createReadStream( |
||||
file, |
||||
Object.assign({}, {}, { |
||||
highWaterMark: 1024 * 1024, |
||||
/* better to use more memory but hash faster */ |
||||
}) |
||||
) |
||||
.on('error', reject) |
||||
.on('end', () => { |
||||
hash.end(); |
||||
console.log('hash done'); |
||||
console.log(hash.read()); |
||||
resolve(hash.digest('base64')); |
||||
}) |
||||
.pipe( |
||||
hash, |
||||
{ |
||||
end: false, |
||||
} |
||||
); |
||||
}); |
||||
} |
||||
|
||||
async function main() { |
||||
|
||||
const allReleases = await getAllReleases() |
||||
|
||||
console.log(`preparing release version: ${version}`) |
||||
let release |
||||
allReleases.find((r) => { |
||||
if (r.tag_name === `v${version}`) { |
||||
release = r |
||||
} |
||||
}) |
||||
if (!release) { |
||||
console.log('No release found.') |
||||
// create release
|
||||
console.log(`Creating release ${version}`) |
||||
const r = await octokit.request('POST /repos/{owner}/{repo}/releases', { |
||||
owner: owner, |
||||
repo: repo, |
||||
tag_name: `v${version}`, |
||||
name: `${version}`, |
||||
draft: true, |
||||
prerelease: true, |
||||
headers: headers, |
||||
}) |
||||
release = r.data |
||||
} |
||||
|
||||
let ymlFiles = await readReleaseFilesFromLocalDirectory() |
||||
ymlFiles = ymlFiles.filter((file) => file.endsWith('.yml') && file.startsWith('latest')) |
||||
|
||||
console.log(`Found ${ymlFiles.length} yml files to upload`) |
||||
|
||||
// read and parse yml latest files
|
||||
// the yml files contain the sha512 hash and file size of the executable
|
||||
// we need to recalculate the hash and file size of the executable
|
||||
// and update the yml files
|
||||
// this is because the executable is resigned after the yml files are created
|
||||
for (const file of ymlFiles) { |
||||
const content = fs.readFileSync(path.join(__dirname, '../../../release', file), 'utf8') |
||||
const parsed = YAML.parse(content) |
||||
const hashes: { |
||||
url: string, |
||||
sha512: string, |
||||
size: number |
||||
}[] = [] |
||||
if (parsed.files) { |
||||
console.log(`Found`, parsed.files) |
||||
for (const f of parsed.files) { |
||||
const executable = f.url |
||||
const exists = fs.existsSync(path.join(__dirname, '../../../release', executable)) |
||||
if (!exists) { |
||||
console.log(`File ${executable} does not exist on local fs. Skipping...`) |
||||
continue |
||||
} else { |
||||
console.log(`File ${executable} exists on local fs. Recalculating hash...`) |
||||
// calculate sha512 hash of executable
|
||||
const hash: string = await hashFile(path.join(__dirname, '../../../release', executable)) |
||||
console.log(hash) |
||||
// calculate file size of executable
|
||||
const stats = fs.statSync(path.join(__dirname, '../../../release', executable)) |
||||
const fileSizeInBytes = stats.size |
||||
console.log(fileSizeInBytes) |
||||
hashes.push({ |
||||
url: executable, |
||||
sha512: hash, |
||||
size: fileSizeInBytes |
||||
}) |
||||
if (parsed.path === executable) { |
||||
parsed.sha512 = hash |
||||
parsed.size = fileSizeInBytes |
||||
} |
||||
} |
||||
} |
||||
} |
||||
console.log(hashes) |
||||
parsed.files = hashes |
||||
const newYml = YAML.stringify(parsed) |
||||
fs.writeFileSync(path.join(__dirname, '../../../release', file), newYml) |
||||
} |
||||
|
||||
let files = await readReleaseFilesFromLocalDirectory() |
||||
|
||||
try { |
||||
if (fs.existsSync(path.join(__dirname, '../../../release', `latest-mac-arm64.yml`)) && fs.existsSync(path.join(__dirname, '../../../release', `latest-mac-x64.yml`))) { |
||||
// combine the two files
|
||||
const macArm64 = fs.readFileSync(path.join(__dirname, '../../../release', `latest-mac-arm64.yml`), 'utf8') |
||||
const mac = fs.readFileSync(path.join(__dirname, '../../../release', `latest-mac-x64.yml`), 'utf8') |
||||
const parsedMacArm64 = YAML.parse(macArm64) |
||||
const parsedMac = YAML.parse(mac) |
||||
console.log(parsedMacArm64) |
||||
console.log(parsedMac) |
||||
const combined = { |
||||
...parsedMac, |
||||
files: [ |
||||
...parsedMac.files, |
||||
...parsedMacArm64.files |
||||
] |
||||
} |
||||
console.log(combined) |
||||
const newYml = YAML.stringify(combined) |
||||
fs.writeFileSync(path.join(__dirname, '../../../release', `latest-mac.yml`), newYml) |
||||
// remove the arm64 file
|
||||
fs.unlinkSync(path.join(__dirname, '../../../release', `latest-mac-arm64.yml`)) |
||||
fs.unlinkSync(path.join(__dirname, '../../../release', `latest-mac-x64.yml`)) |
||||
} |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
|
||||
files = await readReleaseFilesFromLocalDirectory() |
||||
files = files. |
||||
filter((file) => file.endsWith('.zip') || file.endsWith('.dmg') || file.endsWith('.exe') || file.endsWith('.AppImage') || file.endsWith('.snap') || file.endsWith('.deb') || file.startsWith(`latest`)) |
||||
.filter((file) => !file.startsWith('._')) |
||||
console.log(`Found ${files.length} files to upload`) |
||||
console.log(files) |
||||
if (!release.draft) { |
||||
console.log(`Release ${version} is not a draft. Aborting...`) |
||||
return |
||||
} |
||||
// upload files
|
||||
for (const file of files) { |
||||
// check if it is already uploaded
|
||||
const asset = release.assets.find((a) => a.label === file) |
||||
if (asset) { |
||||
console.log(`Asset ${file} already uploaded... replacing it`) |
||||
// remove it first
|
||||
await removeAsset(asset) |
||||
} |
||||
await uploadReleaseAsset(release, file, path.join(__dirname, '../../../release', file)) |
||||
} |
||||
} |
||||
|
||||
main() |
||||
|
@ -0,0 +1,54 @@ |
||||
import { ElectronPlugin } from '@remixproject/engine-electron' |
||||
|
||||
const profile = { |
||||
displayName: 'appUpdater', |
||||
name: 'appUpdater', |
||||
description: 'appUpdater', |
||||
} |
||||
|
||||
export class appUpdaterPlugin extends ElectronPlugin { |
||||
constructor() { |
||||
super(profile) |
||||
} |
||||
|
||||
onActivation(): void { |
||||
this.on('appUpdater', 'askForUpdate', () => { |
||||
console.log('askForUpdate') |
||||
const upgradeModal = { |
||||
id: 'confirmUpdate', |
||||
title: 'An update is available', |
||||
message: `A new version of Remix Desktop is available. Do you want to update?`, |
||||
modalType: 'modal', |
||||
okLabel: 'Yes', |
||||
cancelLabel: 'No', |
||||
okFn: () => { |
||||
this.call('appUpdater', 'download') |
||||
}, |
||||
cancelFn: () => { |
||||
|
||||
}, |
||||
hideFn: () => null |
||||
} |
||||
this.call('notification', 'modal', upgradeModal) |
||||
}) |
||||
this.on('appUpdater', 'downloadReady', () => { |
||||
console.log('downloadReady') |
||||
const upgradeModal = { |
||||
id: 'confirmInstall', |
||||
title: 'An update is ready to install', |
||||
message: `A new version of Remix Desktop is ready to install. Do you want to install it now? This will close Remix Desktop.`, |
||||
modalType: 'modal', |
||||
okLabel: 'Yes', |
||||
cancelLabel: 'No', |
||||
okFn: () => { |
||||
this.call('appUpdater', 'install') |
||||
}, |
||||
cancelFn: () => { |
||||
|
||||
}, |
||||
hideFn: () => null |
||||
} |
||||
this.call('notification', 'modal', upgradeModal) |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
import { ElectronPlugin } from '@remixproject/engine-electron'; |
||||
|
||||
export class FoundryHandleDesktop extends ElectronPlugin { |
||||
constructor() { |
||||
super({ |
||||
displayName: 'foundry', |
||||
name: 'foundry', |
||||
description: 'electron foundry', |
||||
methods: ['sync', 'compile'] |
||||
}) |
||||
this.methods = ['sync', 'compile'] |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
import { ElectronPlugin } from '@remixproject/engine-electron'; |
||||
|
||||
export class HardhatHandleDesktop extends ElectronPlugin { |
||||
constructor() { |
||||
super({ |
||||
displayName: 'hardhat', |
||||
name: 'hardhat', |
||||
description: 'electron hardhat', |
||||
methods: ['sync', 'compile'] |
||||
}) |
||||
this.methods = ['sync', 'compile'] |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
import { ElectronPlugin } from '@remixproject/engine-electron'; |
||||
|
||||
export class SlitherHandleDesktop extends ElectronPlugin { |
||||
constructor() { |
||||
super({ |
||||
displayName: 'slither', |
||||
name: 'slither', |
||||
description: 'electron slither', |
||||
methods: ['analyse'] |
||||
}) |
||||
this.methods = ['analyse'] |
||||
} |
||||
} |
@ -1,4 +1,6 @@ |
||||
{ |
||||
"electron.openFolder": "Open Folder", |
||||
"electron.recentFolders": "Recent Folders" |
||||
"electron.recentFolders": "Recent Folders", |
||||
"electron.gitClone": "Clone a Git Repository", |
||||
"electron.openFolderMessage": "In order to use Git features, you can open a folder or clone a repository." |
||||
} |
@ -0,0 +1,3 @@ |
||||
{ |
||||
"gitui.openFolderMessage": "In order to use Git features, you can open a folder or clone a repository." |
||||
} |
@ -0,0 +1,14 @@ |
||||
const fs = require('fs-extra'); |
||||
const path = require('path'); |
||||
|
||||
exports.default = async function (context) { |
||||
console.log('Running after-pack hook', context); |
||||
const resourcesPath = context.appOutDir; |
||||
console.log('resourcesPath', resourcesPath); |
||||
console.log('context outdir', context.appOutDir); |
||||
// Copy the node-pty module to the app folder
|
||||
await fs.copy( |
||||
path.join('./node_modules', 'node-pty'), |
||||
path.join(resourcesPath, 'node_modules', 'node-pty') |
||||
); |
||||
}; |
@ -0,0 +1,30 @@ |
||||
const fs = require('fs'); |
||||
|
||||
exports.default = async function afterbuild(context) { |
||||
// do not run when not on macOS or when not on CIRCLECI
|
||||
if (process.platform !== 'darwin' || !process.env.CIRCLE_BRANCH) { |
||||
return; |
||||
} |
||||
|
||||
console.log('AFTER BUILD', context); |
||||
|
||||
const artifactPaths = context.artifactPaths; |
||||
const newDmgs = artifactPaths.filter((dmg) => dmg.endsWith('.dmg')).map((dmg) => dmg); // Removed unnecessary quotes for consistency
|
||||
|
||||
let existingDmgs = []; |
||||
try { |
||||
// Attempt to read the existing dmgs.json file
|
||||
const data = fs.readFileSync('dmgs.json', 'utf8'); |
||||
const parsedData = JSON.parse(data); |
||||
existingDmgs = parsedData.dmgs || []; // Ensure existingDmgs is an array
|
||||
} catch (error) { |
||||
// If there's an error reading the file (e.g., file does not exist), proceed with an empty array
|
||||
console.log('No existing dmgs.json or error reading file, creating new one.'); |
||||
} |
||||
|
||||
// Combine existing and new dmgs, avoiding duplicates
|
||||
const combinedDmgs = [...new Set([...existingDmgs, ...newDmgs])]; |
||||
|
||||
// Write/overwrite the dmgs.json with the combined list of dmgs
|
||||
fs.writeFileSync('dmgs.json', JSON.stringify({ dmgs: combinedDmgs }, null, 2)); |
||||
}; |
@ -0,0 +1,104 @@ |
||||
const { notarize } = require('@electron/notarize') |
||||
const fs = require('fs') |
||||
const { exec } = require('child_process') // Import the exec function
|
||||
|
||||
// read the environment variables from process
|
||||
|
||||
console.log(process.env.DO_NOT_NOTARIZE) |
||||
|
||||
if (process.env.DO_NOT_NOTARIZE) { |
||||
console.log('NOTARIZING DISABLED') |
||||
exports.default = async function notarizing(context) { |
||||
return [] |
||||
} |
||||
} else { |
||||
|
||||
exports.default = async function notarizing(context) { |
||||
const { electronPlatformName, appOutDir } = context // Provided by electron-builder
|
||||
|
||||
console.log('NOTARIZING') |
||||
|
||||
if (electronPlatformName !== 'darwin' || !process.env.CIRCLE_BRANCH) { |
||||
return |
||||
} |
||||
|
||||
const appName = context.packager.appInfo.productFilename |
||||
const appPath = `${appOutDir}/${appName}.app` |
||||
|
||||
// Function to promisify the exec command
|
||||
function execShellCommand(cmd) { |
||||
return new Promise((resolve, reject) => { |
||||
exec(cmd, (error, stdout, stderr) => { |
||||
if (error) { |
||||
reject(new Error(`Error: ${error.message}`)); |
||||
return; |
||||
} |
||||
if (stderr) { |
||||
reject(new Error(`Stderr: ${stderr}`)); |
||||
return; |
||||
} |
||||
console.log(`stdout: ${stdout}`); |
||||
resolve(stdout); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
// Function to check if the app is stapled
|
||||
// Async function to check the stapling status
|
||||
async function checkStapleStatus() { |
||||
try { |
||||
console.log(`xcrun stapler validate "${appPath}"`) |
||||
await execShellCommand(`xcrun stapler validate "${appPath}"`); |
||||
console.log('App is already stapled. No action needed.'); |
||||
return true |
||||
} catch (error) { |
||||
console.log(`App is not stapled: ${error.message}`); |
||||
return false |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
async function runNotarize() { |
||||
|
||||
console.log('NOTARIZING + ', `xcrun stapler staple "${appPath}"`) |
||||
console.log({ |
||||
appBundleId: 'org.ethereum.remix-ide', // Your app's bundle ID
|
||||
appPath: `${appOutDir}/${appName}.app`, // Path to your .app
|
||||
appleId: process.env.APPLE_ID, // Your Apple ID
|
||||
appleIdPassword: process.env.APPLE_ID_PASSWORD, // App-specific password
|
||||
teamId: process.env.APPLE_TEAM_ID, // Your Apple Developer team ID (optional)
|
||||
}) |
||||
|
||||
try { |
||||
const r = await notarize({ |
||||
appBundleId: 'org.ethereum.remix-ide', // Your app's bundle ID
|
||||
appPath: `${appOutDir}/${appName}.app`, // Path to your .app
|
||||
appleId: process.env.APPLE_ID, // Your Apple ID
|
||||
appleIdPassword: process.env.APPLE_ID_PASSWORD, // App-specific password
|
||||
teamId: process.env.APPLE_TEAM_ID, // Your Apple Developer team ID (optional)
|
||||
}) |
||||
|
||||
console.log(r) |
||||
|
||||
// Stapling the app
|
||||
console.log('STAPLING', `xcrun stapler staple "${appPath}"`) |
||||
|
||||
await execShellCommand(`xcrun stapler staple "${appPath}"`) |
||||
|
||||
} catch (error) { |
||||
console.error('Error during notarization:', error) |
||||
throw new Error('Error during notarization', error) |
||||
} |
||||
|
||||
} |
||||
|
||||
if (!await checkStapleStatus()) { |
||||
await runNotarize() |
||||
await checkStapleStatus() |
||||
} else { |
||||
return [] |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
{ |
||||
"productName": "Remix-Desktop-alpha", |
||||
"appId": "org.ethereum.remix-ide", |
||||
"asar": true, |
||||
"generateUpdatesFilesForAllChannels": false, |
||||
"icon": "assets", |
||||
"files": [ |
||||
"build/**/*" |
||||
], |
||||
"afterSign": "aftersign.js", |
||||
"afterAllArtifactBuild": "afterbuild.js", |
||||
"publish": [ |
||||
{ |
||||
"provider": "github", |
||||
"owner": "remix-project-org", |
||||
"repo": "remix-desktop-alpha", |
||||
"releaseType": "draft", |
||||
"publishAutoUpdate": true |
||||
} |
||||
], |
||||
"mac": { |
||||
"category": "public.app-category.productivity", |
||||
"icon": "assets/icon.png", |
||||
"darkModeSupport": true, |
||||
"hardenedRuntime": true, |
||||
"gatekeeperAssess": false, |
||||
"entitlements": "entitlements.mac.plist", |
||||
"entitlementsInherit": "entitlements.mac.plist" |
||||
}, |
||||
"dmg": { |
||||
"writeUpdateInfo": true, |
||||
"sign": true |
||||
}, |
||||
"nsis": { |
||||
"createDesktopShortcut": "always", |
||||
"allowToChangeInstallationDirectory": true, |
||||
"oneClick": false, |
||||
"shortcutName": "Remix Desktop alpha", |
||||
"differentialPackage": false |
||||
}, |
||||
"win": { |
||||
"target": [ |
||||
"nsis" |
||||
], |
||||
"artifactName": "${productName}-Setup-${version}.${ext}", |
||||
"icon": "assets/icon.png" |
||||
}, |
||||
"deb": {}, |
||||
"linux": { |
||||
"target": [ |
||||
"deb", |
||||
"snap", |
||||
"AppImage" |
||||
], |
||||
"category": "WebBrowser", |
||||
"icon": "assets" |
||||
}, |
||||
"directories": { |
||||
"output": "release" |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
{ |
||||
"productName": "Remix-Desktop-Beta", |
||||
"appId": "org.ethereum.remix-ide", |
||||
"asar": true, |
||||
"generateUpdatesFilesForAllChannels": false, |
||||
"icon": "assets", |
||||
"files": [ |
||||
"build/**/*" |
||||
], |
||||
"afterSign": "aftersign.js", |
||||
"afterAllArtifactBuild": "afterbuild.js", |
||||
"publish": [ |
||||
{ |
||||
"provider": "github", |
||||
"owner": "remix-project-org", |
||||
"repo": "remix-desktop-beta", |
||||
"releaseType": "draft", |
||||
"publishAutoUpdate": true |
||||
} |
||||
], |
||||
"mac": { |
||||
"category": "public.app-category.productivity", |
||||
"icon": "assets/icon.png", |
||||
"darkModeSupport": true, |
||||
"hardenedRuntime": true, |
||||
"gatekeeperAssess": false, |
||||
"entitlements": "entitlements.mac.plist", |
||||
"entitlementsInherit": "entitlements.mac.plist" |
||||
}, |
||||
"dmg": { |
||||
"writeUpdateInfo": true, |
||||
"sign": true |
||||
}, |
||||
"nsis": { |
||||
"createDesktopShortcut": "always", |
||||
"allowToChangeInstallationDirectory": true, |
||||
"oneClick": false, |
||||
"shortcutName": "Remix Desktop Beta", |
||||
"differentialPackage": false |
||||
}, |
||||
"win": { |
||||
"target": [ |
||||
"nsis" |
||||
], |
||||
"artifactName": "Remix-Desktop-Setup-${version}.${ext}", |
||||
"icon": "assets/icon.png" |
||||
}, |
||||
"deb": {}, |
||||
"linux": { |
||||
"target": [ |
||||
"deb", |
||||
"snap", |
||||
"AppImage" |
||||
], |
||||
"category": "WebBrowser", |
||||
"icon": "assets" |
||||
}, |
||||
"directories": { |
||||
"output": "release" |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
<plist version="1.0"> |
||||
<dict> |
||||
<key>com.apple.security.cs.allow-jit</key> |
||||
<true/> |
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key> |
||||
<true/> |
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key> |
||||
<true/> |
||||
</dict> |
||||
</plist> |
@ -0,0 +1,12 @@ |
||||
const esbuild = require('esbuild'); |
||||
|
||||
esbuild.build({ |
||||
entryPoints: ['src/main.ts', 'src/preload.ts'], // Your TypeScript entry point
|
||||
outdir: 'build', // Output bundled file
|
||||
bundle: true, // Bundle all dependencies
|
||||
platform: 'node', // Target Node.js platform
|
||||
external: ['electron', 'fsevents', 'node-pty'], // Exclude native modules
|
||||
target: ['node20'], // Match the Node.js version for Electron
|
||||
tsconfig: 'tsconfig.json', // Your TypeScript config
|
||||
minify: false, // Optional: Minify for production
|
||||
}).catch(() => process.exit(1)); |
@ -0,0 +1,62 @@ |
||||
{ |
||||
"productName": "Remix-Desktop-Insiders", |
||||
"appId": "org.ethereum.remix-ide", |
||||
"asar": true, |
||||
"generateUpdatesFilesForAllChannels": false, |
||||
"icon": "assets", |
||||
"files": [ |
||||
"build/**/*", |
||||
"node_modules/node-pty/**/*" |
||||
], |
||||
"afterSign": "aftersign.js", |
||||
"afterAllArtifactBuild": "afterbuild.js", |
||||
"publish": [ |
||||
{ |
||||
"provider": "github", |
||||
"owner": "remix-project-org", |
||||
"repo": "remix-desktop-insiders", |
||||
"releaseType": "draft", |
||||
"publishAutoUpdate": true |
||||
} |
||||
], |
||||
"mac": { |
||||
"category": "public.app-category.productivity", |
||||
"icon": "assets/icon.png", |
||||
"darkModeSupport": true, |
||||
"hardenedRuntime": true, |
||||
"gatekeeperAssess": false, |
||||
"entitlements": "entitlements.mac.plist", |
||||
"entitlementsInherit": "entitlements.mac.plist" |
||||
}, |
||||
"dmg": { |
||||
"writeUpdateInfo": true, |
||||
"sign": true |
||||
}, |
||||
"nsis": { |
||||
"createDesktopShortcut": "always", |
||||
"allowToChangeInstallationDirectory": true, |
||||
"oneClick": false, |
||||
"shortcutName": "Remix Desktop Insiders", |
||||
"differentialPackage": false |
||||
}, |
||||
"win": { |
||||
"target": [ |
||||
"nsis" |
||||
], |
||||
"artifactName": "Remix-Desktop-Setup-${version}.${ext}", |
||||
"icon": "assets/icon.png" |
||||
}, |
||||
"deb": {}, |
||||
"linux": { |
||||
"target": [ |
||||
"deb", |
||||
"snap", |
||||
"AppImage" |
||||
], |
||||
"category": "WebBrowser", |
||||
"icon": "assets" |
||||
}, |
||||
"directories": { |
||||
"output": "release" |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
{ |
||||
"productName": "Remix-Desktop", |
||||
"appId": "org.ethereum.remix-ide", |
||||
"asar": true, |
||||
"generateUpdatesFilesForAllChannels": false, |
||||
"icon": "assets", |
||||
"files": [ |
||||
"build/**/*" |
||||
], |
||||
"afterSign": "aftersign.js", |
||||
"afterAllArtifactBuild": "afterbuild.js", |
||||
"publish": [ |
||||
{ |
||||
"provider": "github", |
||||
"owner": "remix-project-org", |
||||
"repo": "remix-desktop", |
||||
"releaseType": "draft", |
||||
"publishAutoUpdate": true |
||||
} |
||||
], |
||||
"mac": { |
||||
"category": "public.app-category.productivity", |
||||
"icon": "assets/icon.png", |
||||
"darkModeSupport": true, |
||||
"hardenedRuntime": true, |
||||
"gatekeeperAssess": false, |
||||
"entitlements": "entitlements.mac.plist", |
||||
"entitlementsInherit": "entitlements.mac.plist" |
||||
}, |
||||
"dmg": { |
||||
"writeUpdateInfo": true, |
||||
"sign": true |
||||
}, |
||||
"nsis": { |
||||
"createDesktopShortcut": "always", |
||||
"allowToChangeInstallationDirectory": true, |
||||
"oneClick": false, |
||||
"shortcutName": "Remix Desktop", |
||||
"differentialPackage": false |
||||
}, |
||||
"win": { |
||||
"target": [ |
||||
"nsis" |
||||
], |
||||
"artifactName": "${productName}-Setup-${version}.${ext}", |
||||
"icon": "assets/icon.png" |
||||
}, |
||||
"deb": {}, |
||||
"linux": { |
||||
"target": [ |
||||
"deb", |
||||
"snap", |
||||
"AppImage" |
||||
], |
||||
"category": "WebBrowser", |
||||
"icon": "assets" |
||||
}, |
||||
"directories": { |
||||
"output": "release" |
||||
} |
||||
} |
@ -0,0 +1,344 @@ |
||||
//
|
||||
// Refer to the online docs for more details:
|
||||
// https://nightwatchjs.org/guide/configuration/nightwatch-configuration-file.html
|
||||
//
|
||||
// _ _ _ _ _ _ _
|
||||
// | \ | |(_) | | | | | | | |
|
||||
// | \| | _ __ _ | |__ | |_ __ __ __ _ | |_ ___ | |__
|
||||
// | . ` || | / _` || '_ \ | __|\ \ /\ / / / _` || __| / __|| '_ \
|
||||
// | |\ || || (_| || | | || |_ \ V V / | (_| || |_ | (__ | | | |
|
||||
// \_| \_/|_| \__, ||_| |_| \__| \_/\_/ \__,_| \__| \___||_| |_|
|
||||
// __/ |
|
||||
// |___/
|
||||
//
|
||||
|
||||
module.exports = { |
||||
// An array of folders (excluding subfolders) where your tests are located;
|
||||
// if this is not specified, the test source must be passed as the second argument to the test runner.
|
||||
src_folders: [], |
||||
|
||||
// See https://nightwatchjs.org/guide/concepts/page-object-model.html
|
||||
page_objects_path: ['node_modules/nightwatch/examples/pages/'], |
||||
|
||||
// See https://nightwatchjs.org/guide/extending-nightwatch/adding-custom-commands.html
|
||||
custom_commands_path: ['node_modules/nightwatch/examples/custom-commands/'], |
||||
|
||||
// See https://nightwatchjs.org/guide/extending-nightwatch/adding-custom-assertions.html
|
||||
custom_assertions_path: '', |
||||
|
||||
// See https://nightwatchjs.org/guide/extending-nightwatch/adding-plugins.html
|
||||
plugins: [], |
||||
|
||||
// See https://nightwatchjs.org/guide/concepts/test-globals.html#external-test-globals
|
||||
globals_path : '', |
||||
|
||||
webdriver: {}, |
||||
|
||||
test_settings: { |
||||
default: { |
||||
disable_error_log: false, |
||||
launch_url: 'https://nightwatchjs.org', |
||||
|
||||
screenshots: { |
||||
enabled: false, |
||||
path: 'screens', |
||||
on_failure: true |
||||
}, |
||||
|
||||
desiredCapabilities: { |
||||
browserName : 'firefox' |
||||
}, |
||||
|
||||
webdriver: { |
||||
start_process: true, |
||||
server_path: '' |
||||
} |
||||
}, |
||||
|
||||
|
||||
|
||||
firefox: { |
||||
desiredCapabilities : { |
||||
browserName : 'firefox', |
||||
alwaysMatch: { |
||||
acceptInsecureCerts: true, |
||||
'moz:firefoxOptions': { |
||||
args: [ |
||||
// '-headless',
|
||||
// '-verbose'
|
||||
] |
||||
} |
||||
} |
||||
}, |
||||
webdriver: { |
||||
start_process: true, |
||||
server_path: '', |
||||
cli_args: [ |
||||
// very verbose geckodriver logs
|
||||
// '-vv'
|
||||
] |
||||
} |
||||
}, |
||||
|
||||
chrome: { |
||||
desiredCapabilities : { |
||||
browserName : 'chrome', |
||||
'goog:chromeOptions' : { |
||||
// More info on Chromedriver: https://sites.google.com/a/chromium.org/chromedriver/
|
||||
//
|
||||
// w3c:false tells Chromedriver to run using the legacy JSONWire protocol (not required in Chrome 78)
|
||||
w3c: true, |
||||
args: [ |
||||
//'--no-sandbox',
|
||||
//'--ignore-certificate-errors',
|
||||
//'--allow-insecure-localhost',
|
||||
//'--headless'
|
||||
] |
||||
} |
||||
}, |
||||
|
||||
webdriver: { |
||||
start_process: true, |
||||
server_path: '', |
||||
cli_args: [ |
||||
// --verbose
|
||||
] |
||||
} |
||||
}, |
||||
|
||||
edge: { |
||||
desiredCapabilities : { |
||||
browserName : 'MicrosoftEdge', |
||||
'ms:edgeOptions' : { |
||||
w3c: true, |
||||
// More info on EdgeDriver: https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/capabilities-edge-options
|
||||
args: [ |
||||
//'--headless'
|
||||
] |
||||
} |
||||
}, |
||||
|
||||
webdriver: { |
||||
start_process: true, |
||||
// Download msedgedriver from https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/
|
||||
// and set the location below:
|
||||
server_path: '', |
||||
cli_args: [ |
||||
// --verbose
|
||||
] |
||||
} |
||||
}, |
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Configuration for when using cucumber-js (https://cucumber.io) |
|
||||
// |
|
||||
// It uses the bundled examples inside the nightwatch examples folder; feel free |
|
||||
// to adapt this to your own project needs |
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
'cucumber-js': { |
||||
src_folders: ['examples/cucumber-js/features/step_definitions'], |
||||
|
||||
test_runner: { |
||||
// set cucumber as the runner
|
||||
type: 'cucumber', |
||||
|
||||
// define cucumber specific options
|
||||
options: { |
||||
//set the feature path
|
||||
feature_path: 'node_modules/nightwatch/examples/cucumber-js/*/*.feature', |
||||
|
||||
// start the webdriver session automatically (enabled by default)
|
||||
// auto_start_session: true
|
||||
|
||||
// use parallel execution in Cucumber
|
||||
// parallel: 2 // set number of workers to use (can also be defined in the cli as --parallel 2
|
||||
} |
||||
} |
||||
}, |
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Configuration for when using the browserstack.com cloud service |
|
||||
// |
|
||||
// Please set the username and access key by setting the environment variables: |
|
||||
// - BROWSERSTACK_USERNAME |
|
||||
// - BROWSERSTACK_ACCESS_KEY |
|
||||
// .env files are supported |
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
browserstack: { |
||||
selenium: { |
||||
host: 'hub.browserstack.com', |
||||
port: 443 |
||||
}, |
||||
// More info on configuring capabilities can be found on:
|
||||
// https://www.browserstack.com/automate/capabilities?tag=selenium-4
|
||||
desiredCapabilities: { |
||||
'bstack:options' : { |
||||
userName: '${BROWSERSTACK_USERNAME}', |
||||
accessKey: '${BROWSERSTACK_ACCESS_KEY}', |
||||
} |
||||
}, |
||||
|
||||
disable_error_log: true, |
||||
webdriver: { |
||||
timeout_options: { |
||||
timeout: 15000, |
||||
retry_attempts: 3 |
||||
}, |
||||
keep_alive: true, |
||||
start_process: false |
||||
} |
||||
}, |
||||
|
||||
'browserstack.local': { |
||||
extends: 'browserstack', |
||||
desiredCapabilities: { |
||||
'browserstack.local': true |
||||
} |
||||
}, |
||||
|
||||
'browserstack.chrome': { |
||||
extends: 'browserstack', |
||||
desiredCapabilities: { |
||||
browserName: 'chrome', |
||||
chromeOptions : { |
||||
w3c: true |
||||
} |
||||
} |
||||
}, |
||||
|
||||
'browserstack.firefox': { |
||||
extends: 'browserstack', |
||||
desiredCapabilities: { |
||||
browserName: 'firefox' |
||||
} |
||||
}, |
||||
|
||||
'browserstack.ie': { |
||||
extends: 'browserstack', |
||||
desiredCapabilities: { |
||||
browserName: 'internet explorer', |
||||
browserVersion: '11.0' |
||||
} |
||||
}, |
||||
|
||||
'browserstack.safari': { |
||||
extends: 'browserstack', |
||||
desiredCapabilities: { |
||||
browserName: 'safari' |
||||
} |
||||
}, |
||||
|
||||
'browserstack.local_chrome': { |
||||
extends: 'browserstack.local', |
||||
desiredCapabilities: { |
||||
browserName: 'chrome' |
||||
} |
||||
}, |
||||
|
||||
'browserstack.local_firefox': { |
||||
extends: 'browserstack.local', |
||||
desiredCapabilities: { |
||||
browserName: 'firefox' |
||||
} |
||||
}, |
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Configuration for when using the SauceLabs cloud service |
|
||||
// |
|
||||
// Please set the username and access key by setting the environment variables: |
|
||||
// - SAUCE_USERNAME |
|
||||
// - SAUCE_ACCESS_KEY |
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
saucelabs: { |
||||
selenium: { |
||||
host: 'ondemand.saucelabs.com', |
||||
port: 443 |
||||
}, |
||||
// More info on configuring capabilities can be found on:
|
||||
// https://docs.saucelabs.com/dev/test-configuration-options/
|
||||
desiredCapabilities: { |
||||
'sauce:options' : { |
||||
username: '${SAUCE_USERNAME}', |
||||
accessKey: '${SAUCE_ACCESS_KEY}', |
||||
screenResolution: '1280x1024' |
||||
// https://docs.saucelabs.com/dev/cli/sauce-connect-proxy/#--region
|
||||
// region: 'us-west-1'
|
||||
// https://docs.saucelabs.com/dev/test-configuration-options/#tunnelidentifier
|
||||
// parentTunnel: '',
|
||||
// tunnelIdentifier: '',
|
||||
} |
||||
}, |
||||
disable_error_log: false, |
||||
webdriver: { |
||||
start_process: false |
||||
} |
||||
}, |
||||
'saucelabs.chrome': { |
||||
extends: 'saucelabs', |
||||
desiredCapabilities: { |
||||
browserName: 'chrome', |
||||
browserVersion: 'latest', |
||||
javascriptEnabled: true, |
||||
acceptSslCerts: true, |
||||
timeZone: 'London', |
||||
chromeOptions : { |
||||
w3c: true |
||||
} |
||||
} |
||||
}, |
||||
'saucelabs.firefox': { |
||||
extends: 'saucelabs', |
||||
desiredCapabilities: { |
||||
browserName: 'firefox', |
||||
browserVersion: 'latest', |
||||
javascriptEnabled: true, |
||||
acceptSslCerts: true, |
||||
timeZone: 'London' |
||||
} |
||||
}, |
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Configuration for when using the Selenium service, either locally or remote, |
|
||||
// like Selenium Grid |
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
selenium_server: { |
||||
// Selenium Server is running locally and is managed by Nightwatch
|
||||
// Install the NPM package @nightwatch/selenium-server or download the selenium server jar file from https://github.com/SeleniumHQ/selenium/releases/, e.g.: selenium-server-4.1.1.jar
|
||||
selenium: { |
||||
start_process: true, |
||||
port: 4444, |
||||
server_path: '', // Leave empty if @nightwatch/selenium-server is installed
|
||||
command: 'standalone', // Selenium 4 only
|
||||
cli_args: { |
||||
//'webdriver.gecko.driver': '',
|
||||
//'webdriver.chrome.driver': ''
|
||||
} |
||||
}, |
||||
webdriver: { |
||||
start_process: false, |
||||
default_path_prefix: '/wd/hub' |
||||
} |
||||
}, |
||||
|
||||
'selenium.chrome': { |
||||
extends: 'selenium_server', |
||||
desiredCapabilities: { |
||||
browserName: 'chrome', |
||||
chromeOptions : { |
||||
w3c: true |
||||
} |
||||
} |
||||
}, |
||||
|
||||
'selenium.firefox': { |
||||
extends: 'selenium_server', |
||||
desiredCapabilities: { |
||||
browserName: 'firefox', |
||||
'moz:firefoxOptions': { |
||||
args: [ |
||||
// '-headless',
|
||||
// '-verbose'
|
||||
] |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}; |
@ -0,0 +1,39 @@ |
||||
#!/bin/bash |
||||
|
||||
# Path to the JSON file containing the DMG paths |
||||
JSON_FILE="dmgs.json" |
||||
|
||||
# Read the DMGs array from the JSON file |
||||
DMG_PATHS=$(jq -r '.dmgs[]' "$JSON_FILE") |
||||
|
||||
echo $DMG_PATHS |
||||
|
||||
xcrun notarytool store-credentials "notarytool-password" \ |
||||
--apple-id ${APPLE_ID} \ |
||||
--team-id ${APPLE_TEAM_ID} \ |
||||
--password ${APPLE_ID_PASSWORD} || exit 1 |
||||
|
||||
# Use jq to parse the DMGs array and read each line |
||||
while IFS= read -r DMG_PATH; do |
||||
# Remove single quotes from the path if present |
||||
DMG_PATH_CLEANED=$(echo $DMG_PATH | tr -d "'") |
||||
|
||||
echo "Submitting $DMG_PATH_CLEANED for notarization..." |
||||
|
||||
# Replace `your-app-specific-args` with the actual arguments for your app |
||||
# Ensure your notarytool command and arguments are correct for your application |
||||
xcrun notarytool submit "$DMG_PATH_CLEANED" --keychain-profile "notarytool-password" --wait |
||||
|
||||
# Check the command's success |
||||
if [ $? -eq 0 ]; then |
||||
echo "Successfully submitted $DMG_PATH_CLEANED for notarization." |
||||
xcrun stapler staple "$DMG_PATH_CLEANED" |
||||
echo "Successfully stapled $DMG_PATH_CLEANED." |
||||
spctl -a -t open -vvv --context context:primary-signature "$DMG_PATH_CLEANED" |
||||
echo "Successfully checked $DMG_PATH_CLEANED." |
||||
else |
||||
echo "Failed to submit $DMG_PATH_CLEANED for notarization." |
||||
fi |
||||
done < <(jq -r '.dmgs[]' "$JSON_FILE") |
||||
|
||||
echo "All DMG submissions completed." |
@ -0,0 +1,19 @@ |
||||
#!/usr/bin/env bash |
||||
set -e |
||||
TEST_EXITCODE=0 |
||||
yarn run build:e2e && node ./splice_tests.js |
||||
TESTFILES=$(node ./splice_tests.js | circleci tests split --split-by=timings) |
||||
for TESTFILE in $TESTFILES; do |
||||
yarn run test --test ./build-e2e/remixdesktop/test/tests/app/${TESTFILE} || yarn run test --test ./build-e2e/remixdesktop/test/tests/app/${TESTFILE} || TEST_EXITCODE=1 |
||||
done |
||||
|
||||
|
||||
if [ "$CIRCLE_NODE_INDEX" -eq 1 ]; then |
||||
yarn test:offline || TEST_EXITCODE=1 |
||||
fi |
||||
|
||||
echo "$TEST_EXITCODE" |
||||
if [ "$TEST_EXITCODE" -eq 1 ] |
||||
then |
||||
exit 1 |
||||
fi |
@ -0,0 +1,14 @@ |
||||
#!/usr/bin/env bash |
||||
set -e |
||||
TEST_EXITCODE=0 |
||||
yarn run build:e2e && node ./splice_tests.js |
||||
TESTFILES=$(node ./splice_tests.js | grep -i 'git' | circleci tests split --split-by=timings) |
||||
for TESTFILE in $TESTFILES; do |
||||
yarn run test --use-isogit --test ./build-e2e/remixdesktop/test/tests/app/${TESTFILE} || yarn run test --use-isogit --test ./build-e2e/remixdesktop/test/tests/app/${TESTFILE} || TEST_EXITCODE=1 |
||||
done |
||||
|
||||
echo "$TEST_EXITCODE" |
||||
if [ "$TEST_EXITCODE" -eq 1 ] |
||||
then |
||||
exit 1 |
||||
fi |
@ -0,0 +1,24 @@ |
||||
#!/bin/bash |
||||
|
||||
# Read the version from package.json |
||||
version=$(awk -F'"' '/"version":/ {print $4}' package.json) |
||||
|
||||
# Determine the command to run based on the version |
||||
if [[ $version == *"beta"* ]]; then |
||||
command="yarn dist -c beta.json" |
||||
elif [[ $version == *"alpha"* ]]; then |
||||
command="yarn dist -c alpha.json" |
||||
elif [[ $version == *"insiders"* ]]; then |
||||
command="yarn dist -c insiders.json" |
||||
else |
||||
command="yarn dist -c latest.json" |
||||
fi |
||||
|
||||
# Append any arguments passed in CLI |
||||
for arg in "$@"; do |
||||
command+=" $arg" |
||||
done |
||||
|
||||
# Print and run the command |
||||
echo "Running command: $command" |
||||
$command |
@ -0,0 +1,24 @@ |
||||
#!/bin/bash |
||||
|
||||
# Read the version from package.json |
||||
version=$(awk -F'"' '/"version":/ {print $4}' package.json) |
||||
|
||||
# Determine the command to run based on the version |
||||
if [[ $version == *"beta"* ]]; then |
||||
command="yarn esbuild -c beta.json" |
||||
elif [[ $version == *"alpha"* ]]; then |
||||
command="yarn esbuild -c alpha.json" |
||||
elif [[ $version == *"insiders"* ]]; then |
||||
command="yarn esbuild -c insiders.json" |
||||
else |
||||
command="yarn esbuild -c latest.json" |
||||
fi |
||||
|
||||
# Append any arguments passed in CLI |
||||
for arg in "$@"; do |
||||
command+=" $arg" |
||||
done |
||||
|
||||
# Print and run the command |
||||
echo "Running command: $command" |
||||
$command |
@ -0,0 +1,24 @@ |
||||
#!/bin/bash |
||||
|
||||
# Read the version from package.json |
||||
version=$(awk -F'"' '/"version":/ {print $4}' package.json) |
||||
|
||||
# Determine the command to run based on the version |
||||
if [[ $version == *"beta"* ]]; then |
||||
command="yarn tscbuild -c beta.json" |
||||
elif [[ $version == *"alpha"* ]]; then |
||||
command="yarn tscbuild -c alpha.json" |
||||
elif [[ $version == *"insiders"* ]]; then |
||||
command="yarn tscbuild -c insiders.json" |
||||
else |
||||
command="yarn tscbuild -c latest.json" |
||||
fi |
||||
|
||||
# Append any arguments passed in CLI |
||||
for arg in "$@"; do |
||||
command+=" $arg" |
||||
done |
||||
|
||||
# Print and run the command |
||||
echo "Running command: $command" |
||||
$command |
@ -0,0 +1,24 @@ |
||||
#!/bin/bash |
||||
|
||||
# Read the version from package.json |
||||
version=$(awk -F'"' '/"version":/ {print $4}' package.json) |
||||
|
||||
# Determine the command to run based on the version |
||||
if [[ $version == *"beta"* ]]; then |
||||
command="yarn dist -c beta.json" |
||||
elif [[ $version == *"alpha"* ]]; then |
||||
command="yarn dist -c alpha.json" |
||||
elif [[ $version == *"insiders"* ]]; then |
||||
command="yarn dist -c insiders.json" |
||||
else |
||||
command="yarn dist -c latest.json" |
||||
fi |
||||
|
||||
# Append any arguments passed in CLI |
||||
for arg in "$@"; do |
||||
command+=" $arg" |
||||
done |
||||
|
||||
# Print and run the command |
||||
echo "Running command: $command" |
||||
$command |
@ -0,0 +1,35 @@ |
||||
const fs = require('fs'); |
||||
const path = require('path'); |
||||
|
||||
// Directory to read files from
|
||||
const testDirectory = './build-e2e/remixdesktop/test/tests/app/'; |
||||
|
||||
// Function to read files in a directory and return their paths
|
||||
function getTestFiles(directory) { |
||||
return fs.readdirSync(directory) |
||||
.filter(file => file.endsWith('.test.js')) // Get only .test.js files
|
||||
.map(file => path.join(directory, file)); // Return full path of each file
|
||||
} |
||||
|
||||
// Function to check if a file contains a specific word
|
||||
function fileContainsWord(filePath, word) { |
||||
const content = fs.readFileSync(filePath, 'utf-8'); // Read file content
|
||||
return content.includes(word); // Check if word is in content
|
||||
} |
||||
|
||||
// Function to filter out files that do not contain the specified word
|
||||
function filterFilesByWord(files, word) { |
||||
return files.filter(file => fileContainsWord(file, word)); // Return files that do not contain the word
|
||||
} |
||||
|
||||
// Get all test files in the specified directory
|
||||
const testFiles = getTestFiles(testDirectory); |
||||
|
||||
// Filter out files that do not contain "@offline"
|
||||
const filteredFiles = filterFilesByWord(testFiles, ''); |
||||
|
||||
// Output the list of filtered files
|
||||
//console.log('Files without "@offline":', filteredFiles);
|
||||
for (let i = 0; i < filteredFiles.length; i++) { |
||||
console.log(path.basename(filteredFiles[i])); |
||||
} |
@ -0,0 +1,37 @@ |
||||
import { ElectronBasePluginClient } from "@remixproject/plugin-electron"; |
||||
import { Profile } from "@remixproject/plugin-utils"; |
||||
|
||||
export class ElectronBasePluginRemixdClient extends ElectronBasePluginClient { |
||||
log: (...message: any) => void |
||||
error: (...message: any) => void |
||||
|
||||
currentSharedFolder: string = '' |
||||
constructor(webContentsId: number, profile: Profile) { |
||||
super(webContentsId, profile); |
||||
this.log = (...message: any) => { |
||||
for(const m of message) { |
||||
this.call('terminal', 'log', { |
||||
type: 'log', |
||||
value: m |
||||
}) |
||||
} |
||||
} |
||||
this.error = (...message: any) => { |
||||
for(const m of message) { |
||||
this.call('terminal', 'log', { |
||||
type: 'error', |
||||
value: m |
||||
}) |
||||
} |
||||
} |
||||
|
||||
|
||||
this.onload(async () => { |
||||
this.on('fs' as any, 'workingDirChanged', async (path: string) => { |
||||
console.log('workingDirChanged base remixd', path) |
||||
this.currentSharedFolder = path |
||||
}) |
||||
this.currentSharedFolder = await this.call('fs' as any, 'getWorkingDir') |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
import * as pathModule from 'path' |
||||
/** |
||||
* returns the absolute path of the given @arg path |
||||
* |
||||
* @param {String} path - relative path (Unix style which is the one used by Remix IDE) |
||||
* @param {String} sharedFolder - absolute shared path. platform dependent representation. |
||||
* @return {String} platform dependent absolute path (/home/user1/.../... for unix, c:\user\...\... for windows) |
||||
*/ |
||||
function absolutePath (path: string, sharedFolder:string): string { |
||||
path = normalizePath(path) |
||||
path = pathModule.resolve(sharedFolder, path) |
||||
return path |
||||
} |
||||
function normalizePath (path) { |
||||
if (path === '/') path = './' |
||||
if (process.platform === 'win32') { |
||||
return path.replace(/\//g, '\\') |
||||
} |
||||
return path |
||||
} |
||||
|
||||
export { absolutePath, normalizePath } |
||||
|
||||
|
@ -0,0 +1,122 @@ |
||||
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" |
||||
import { Profile } from "@remixproject/plugin-utils" |
||||
import { autoUpdater } from "electron-updater" |
||||
import { app } from 'electron'; |
||||
import { isE2E } from "../main"; |
||||
import { trackEvent } from "../utils/matamo"; |
||||
|
||||
const profile = { |
||||
displayName: 'appUpdater', |
||||
name: 'appUpdater', |
||||
description: 'appUpdater', |
||||
} |
||||
|
||||
export class AppUpdaterPlugin extends ElectronBasePlugin { |
||||
clients: AppUpdaterPluginClient[] = [] |
||||
constructor() { |
||||
super(profile, clientProfile, AppUpdaterPluginClient) |
||||
this.methods = [...super.methods] |
||||
|
||||
autoUpdater.autoDownload = false |
||||
autoUpdater.disableDifferentialDownload = true |
||||
|
||||
autoUpdater.on('checking-for-update', () => { |
||||
console.log('Checking for update...'); |
||||
this.sendToLog('Checking for update...') |
||||
}) |
||||
autoUpdater.on('update-available', (info: any) => { |
||||
console.log('Update available.', info); |
||||
this.sendToLog('Update available.') |
||||
for (const client of this.clients) { |
||||
client.askForUpdate() |
||||
} |
||||
}) |
||||
autoUpdater.on('update-not-available', () => { |
||||
console.log('Update not available.'); |
||||
this.sendToLog('App is already up to date.') |
||||
|
||||
}) |
||||
autoUpdater.on('error', (err) => { |
||||
console.log('Error in auto-updater. ' + err); |
||||
this.sendToLog('Cannot find updates...') |
||||
}) |
||||
autoUpdater.on('download-progress', (progressObj) => { |
||||
let log_message = "Download speed: " + progressObj.bytesPerSecond; |
||||
log_message = log_message + ' - Downloaded ' + progressObj.percent + '%'; |
||||
log_message = log_message + ' (' + progressObj.transferred + "/" + progressObj.total + ')'; |
||||
console.log(log_message); |
||||
this.sendToLog(log_message) |
||||
}) |
||||
autoUpdater.on('update-downloaded', (info) => { |
||||
console.log('Update downloaded'); |
||||
this.sendToLog('Update downloaded') |
||||
this.sendToLog('processing download... please wait...') |
||||
for(const client of this.clients) { |
||||
client.downloadReady() |
||||
} |
||||
}) |
||||
} |
||||
|
||||
async sendToLog(message: string): Promise<void> { |
||||
for (const client of this.clients) { |
||||
client.call('terminal', 'log', { |
||||
type: 'log', |
||||
value: message, |
||||
}) |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
const clientProfile: Profile = { |
||||
name: 'appUpdater', |
||||
displayName: 'appUpdater', |
||||
description: 'appUpdater', |
||||
methods: ['checkForUpdates', 'download', 'install'], |
||||
} |
||||
|
||||
class AppUpdaterPluginClient extends ElectronBasePluginClient { |
||||
constructor(webContentsId: number, profile: Profile) { |
||||
super(webContentsId, profile) |
||||
} |
||||
|
||||
async onActivation(): Promise<void> { |
||||
this.onload(async () => { |
||||
this.emit('loaded') |
||||
if(isE2E) return |
||||
await this.checkForUpdates() |
||||
}) |
||||
} |
||||
|
||||
async askForUpdate(): Promise<void> { |
||||
this.emit('askForUpdate') |
||||
} |
||||
|
||||
async downloadReady(): Promise<void> { |
||||
// we do a wait here to make sure that the download is done, it's a bug in electron-updater
|
||||
setTimeout(() => { |
||||
this.emit('downloadReady') |
||||
} |
||||
, 10000) |
||||
} |
||||
|
||||
async download(): Promise<void> { |
||||
autoUpdater.downloadUpdate() |
||||
} |
||||
|
||||
async install(): Promise<void> { |
||||
autoUpdater.quitAndInstall() |
||||
} |
||||
|
||||
async checkForUpdates(): Promise<void> { |
||||
console.log('checkForUpdates') |
||||
this.call('terminal', 'log', { |
||||
type: 'log', |
||||
value: 'Remix Desktop version: ' + autoUpdater.currentVersion, |
||||
}) |
||||
trackEvent('App', 'CheckForUpdate', 'Remix Desktop version: ' + autoUpdater.currentVersion, 1); |
||||
|
||||
autoUpdater.checkForUpdates() |
||||
} |
||||
} |
||||
|
@ -0,0 +1,248 @@ |
||||
import { Profile } from "@remixproject/plugin-utils"; |
||||
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" |
||||
import chokidar from 'chokidar' |
||||
import { ElectronBasePluginRemixdClient } from "../lib/remixd" |
||||
import fs from 'fs' |
||||
import * as utils from '../lib/utils' |
||||
|
||||
import { basename, join } from "path"; |
||||
import { spawn } from "child_process"; |
||||
const profile: Profile = { |
||||
name: 'foundry', |
||||
displayName: 'electron foundry', |
||||
description: 'electron foundry', |
||||
} |
||||
|
||||
export class FoundryPlugin extends ElectronBasePlugin { |
||||
clients: any[] |
||||
constructor() { |
||||
super(profile, clientProfile, FoundryPluginClient) |
||||
this.methods = [...super.methods] |
||||
} |
||||
} |
||||
|
||||
const clientProfile: Profile = { |
||||
name: 'foundry', |
||||
displayName: 'electron foundry', |
||||
description: 'electron foundry', |
||||
methods: ['sync', 'compile'] |
||||
} |
||||
|
||||
|
||||
class FoundryPluginClient extends ElectronBasePluginRemixdClient { |
||||
|
||||
watcher: chokidar.FSWatcher |
||||
warnlog: boolean |
||||
buildPath: string |
||||
cachePath: string |
||||
logTimeout: NodeJS.Timeout |
||||
processingTimeout: NodeJS.Timeout |
||||
|
||||
async onActivation(): Promise<void> { |
||||
console.log('Foundry plugin activated') |
||||
this.call('terminal', 'log', { type: 'log', value: 'Foundry plugin activated' }) |
||||
this.on('fs' as any, 'workingDirChanged', async (path: string) => { |
||||
console.log('workingDirChanged foundry', path) |
||||
this.currentSharedFolder = path |
||||
this.startListening() |
||||
}) |
||||
this.currentSharedFolder = await this.call('fs' as any, 'getWorkingDir') |
||||
if(this.currentSharedFolder) this.startListening() |
||||
} |
||||
|
||||
startListening() { |
||||
this.buildPath = utils.absolutePath('out', this.currentSharedFolder) |
||||
this.cachePath = utils.absolutePath('cache', this.currentSharedFolder) |
||||
console.log('Foundry plugin checking for', this.buildPath, this.cachePath) |
||||
if (fs.existsSync(this.buildPath) && fs.existsSync(this.cachePath)) { |
||||
this.listenOnFoundryCompilation() |
||||
} else { |
||||
this.listenOnFoundryFolder() |
||||
} |
||||
} |
||||
|
||||
listenOnFoundryFolder() { |
||||
console.log('Foundry out folder doesn\'t exist... waiting for the compilation.') |
||||
try { |
||||
if (this.watcher) this.watcher.close() |
||||
this.watcher = chokidar.watch(this.currentSharedFolder, { depth: 1, ignorePermissionErrors: true, ignoreInitial: true }) |
||||
// watch for new folders
|
||||
this.watcher.on('addDir', (path: string) => { |
||||
console.log('add dir foundry', path) |
||||
if (fs.existsSync(this.buildPath) && fs.existsSync(this.cachePath)) { |
||||
this.listenOnFoundryCompilation() |
||||
} |
||||
}) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
compile() { |
||||
return new Promise((resolve, reject) => { |
||||
const cmd = `forge build` |
||||
const options = { cwd: this.currentSharedFolder, shell: true } |
||||
const child = spawn(cmd, options) |
||||
let result = '' |
||||
let error = '' |
||||
child.stdout.on('data', (data) => { |
||||
const msg = `[Foundry Compilation]: ${data.toString()}` |
||||
console.log('\x1b[32m%s\x1b[0m', msg) |
||||
result += msg + '\n' |
||||
}) |
||||
child.stderr.on('data', (err) => { |
||||
error += `[Foundry Compilation]: ${err.toString()} \n` |
||||
}) |
||||
child.on('close', () => { |
||||
if (error && result) resolve(error + result) |
||||
else if (error) reject(error) |
||||
else resolve(result) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
checkPath() { |
||||
if (!fs.existsSync(this.buildPath) || !fs.existsSync(this.cachePath)) { |
||||
this.listenOnFoundryFolder() |
||||
return false |
||||
} |
||||
if (!fs.existsSync(join(this.cachePath, 'solidity-files-cache.json'))) return false |
||||
return true |
||||
} |
||||
|
||||
private async processArtifact() { |
||||
if (!this.checkPath()) return |
||||
const folderFiles = await fs.promises.readdir(this.buildPath) // "out" folder
|
||||
try { |
||||
const cache = JSON.parse(await fs.promises.readFile(join(this.cachePath, 'solidity-files-cache.json'), { encoding: 'utf-8' })) |
||||
// name of folders are file names
|
||||
for (const file of folderFiles) { |
||||
const path = join(this.buildPath, file) // out/Counter.sol/
|
||||
const compilationResult = { |
||||
input: {}, |
||||
output: { |
||||
contracts: {}, |
||||
sources: {} |
||||
}, |
||||
inputSources: { sources: {}, target: '' }, |
||||
solcVersion: null, |
||||
compilationTarget: null |
||||
} |
||||
compilationResult.inputSources.target = file |
||||
await this.readContract(path, compilationResult, cache) |
||||
this.emit('compilationFinished', compilationResult.compilationTarget, { sources: compilationResult.input }, 'soljson', compilationResult.output, compilationResult.solcVersion) |
||||
} |
||||
|
||||
clearTimeout(this.logTimeout) |
||||
this.logTimeout = setTimeout(() => { |
||||
// @ts-ignore
|
||||
this.call('terminal', 'log', { type: 'log', value: `receiving compilation result from Foundry. Select a file to populate the contract interaction interface.` }) |
||||
console.log('Syncing compilation result from Foundry') |
||||
}, 1000) |
||||
|
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
async triggerProcessArtifact() { |
||||
// prevent multiple calls
|
||||
clearTimeout(this.processingTimeout) |
||||
this.processingTimeout = setTimeout(async () => await this.processArtifact(), 1000) |
||||
} |
||||
|
||||
listenOnFoundryCompilation() { |
||||
try { |
||||
console.log('Foundry out folder exists... processing the artifact.') |
||||
if (this.watcher) this.watcher.close() |
||||
this.watcher = chokidar.watch(this.cachePath, { depth: 0, ignorePermissionErrors: true, ignoreInitial: true }) |
||||
this.watcher.on('change', async () => await this.triggerProcessArtifact()) |
||||
this.watcher.on('add', async () => await this.triggerProcessArtifact()) |
||||
this.watcher.on('unlink', async () => await this.triggerProcessArtifact()) |
||||
// process the artifact on activation
|
||||
this.triggerProcessArtifact() |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
async readContract(contractFolder, compilationResultPart, cache) { |
||||
const files = await fs.promises.readdir(contractFolder) |
||||
for (const file of files) { |
||||
const path = join(contractFolder, file) |
||||
const content = await fs.promises.readFile(path, { encoding: 'utf-8' }) |
||||
compilationResultPart.inputSources.sources[file] = { content } |
||||
await this.feedContractArtifactFile(file, content, compilationResultPart, cache) |
||||
} |
||||
} |
||||
|
||||
async feedContractArtifactFile(path, content, compilationResultPart, cache) { |
||||
const contentJSON = JSON.parse(content) |
||||
const contractName = basename(path).replace('.json', '') |
||||
|
||||
let sourcePath = '' |
||||
if (contentJSON?.metadata?.settings?.compilationTarget) { |
||||
for (const key in contentJSON.metadata.settings.compilationTarget) { |
||||
if (contentJSON.metadata.settings.compilationTarget[key] === contractName) { |
||||
sourcePath = key |
||||
break |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (!sourcePath) return |
||||
|
||||
const currentCache = cache.files[sourcePath] |
||||
if (!currentCache.artifacts[contractName]) return |
||||
|
||||
// extract source and version
|
||||
const metadata = contentJSON.metadata |
||||
if (metadata.compiler && metadata.compiler.version) { |
||||
compilationResultPart.solcVersion = metadata.compiler.version |
||||
} else { |
||||
compilationResultPart.solcVersion = '' |
||||
console.log('\x1b[32m%s\x1b[0m', 'compiler version not found, please update Foundry to the latest version.') |
||||
} |
||||
|
||||
if (metadata.sources) { |
||||
for (const path in metadata.sources) { |
||||
const absPath = utils.absolutePath(path, this.currentSharedFolder) |
||||
try { |
||||
const content = await fs.promises.readFile(absPath, { encoding: 'utf-8' }) |
||||
compilationResultPart.input[path] = { content } |
||||
} catch (e) { |
||||
compilationResultPart.input[path] = { content: '' } |
||||
} |
||||
} |
||||
} else { |
||||
console.log('\x1b[32m%s\x1b[0m', 'sources input not found, please update Foundry to the latest version.') |
||||
} |
||||
|
||||
compilationResultPart.compilationTarget = sourcePath |
||||
// extract data
|
||||
if (!compilationResultPart.output['sources'][sourcePath]) compilationResultPart.output['sources'][sourcePath] = {} |
||||
compilationResultPart.output['sources'][sourcePath] = { |
||||
ast: contentJSON['ast'], |
||||
id: contentJSON['id'] |
||||
} |
||||
if (!compilationResultPart.output['contracts'][sourcePath]) compilationResultPart.output['contracts'][sourcePath] = {} |
||||
|
||||
contentJSON.bytecode.object = contentJSON.bytecode.object.replace('0x', '') |
||||
contentJSON.deployedBytecode.object = contentJSON.deployedBytecode.object.replace('0x', '') |
||||
compilationResultPart.output['contracts'][sourcePath][contractName] = { |
||||
abi: contentJSON.abi, |
||||
evm: { |
||||
bytecode: contentJSON.bytecode, |
||||
deployedBytecode: contentJSON.deployedBytecode, |
||||
methodIdentifiers: contentJSON.methodIdentifiers |
||||
} |
||||
} |
||||
} |
||||
|
||||
async sync() { |
||||
console.log('syncing Foundry with Remix...') |
||||
this.processArtifact() |
||||
} |
||||
} |
||||
|
||||
|
@ -0,0 +1,220 @@ |
||||
import { Profile } from "@remixproject/plugin-utils"; |
||||
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" |
||||
import chokidar from 'chokidar' |
||||
import { ElectronBasePluginRemixdClient } from "../lib/remixd" |
||||
import fs from 'fs' |
||||
import * as utils from '../lib/utils' |
||||
|
||||
import { basename, join } from "path"; |
||||
import { spawn } from "child_process"; |
||||
const profile: Profile = { |
||||
name: 'hardhat', |
||||
displayName: 'electron slither', |
||||
description: 'electron slither', |
||||
} |
||||
|
||||
export class HardhatPlugin extends ElectronBasePlugin { |
||||
clients: any[] |
||||
constructor() { |
||||
super(profile, clientProfile, HardhatPluginClient) |
||||
this.methods = [...super.methods] |
||||
} |
||||
} |
||||
|
||||
const clientProfile: Profile = { |
||||
name: 'hardhat', |
||||
displayName: 'electron hardhat', |
||||
description: 'electron hardhat', |
||||
methods: ['sync', 'compile'] |
||||
} |
||||
|
||||
|
||||
class HardhatPluginClient extends ElectronBasePluginRemixdClient { |
||||
watcher: chokidar.FSWatcher |
||||
warnlog: boolean |
||||
buildPath: string |
||||
cachePath: string |
||||
logTimeout: NodeJS.Timeout |
||||
processingTimeout: NodeJS.Timeout |
||||
|
||||
async onActivation(): Promise<void> { |
||||
console.log('Hardhat plugin activated') |
||||
this.call('terminal', 'log', { type: 'log', value: 'Hardhat plugin activated' }) |
||||
|
||||
this.on('fs' as any, 'workingDirChanged', async (path: string) => { |
||||
console.log('workingDirChanged hardhat', path) |
||||
this.currentSharedFolder = path |
||||
this.startListening() |
||||
}) |
||||
this.currentSharedFolder = await this.call('fs' as any, 'getWorkingDir') |
||||
if(this.currentSharedFolder) this.startListening() |
||||
} |
||||
|
||||
startListening() { |
||||
this.buildPath = utils.absolutePath('artifacts/contracts', this.currentSharedFolder) |
||||
if (fs.existsSync(this.buildPath)) { |
||||
this.listenOnHardhatCompilation() |
||||
} else { |
||||
console.log('If you are using Hardhat, run `npx hardhat compile` or run the compilation with `Enable Hardhat Compilation` checked from the Remix IDE.') |
||||
this.listenOnHardHatFolder() |
||||
} |
||||
} |
||||
|
||||
compile(configPath: string) { |
||||
return new Promise((resolve, reject) => { |
||||
const cmd = `npx hardhat compile --config ${utils.normalizePath(configPath)}` |
||||
const options = { cwd: this.currentSharedFolder, shell: true } |
||||
const child = spawn(cmd, options) |
||||
let result = '' |
||||
let error = '' |
||||
child.stdout.on('data', (data) => { |
||||
const msg = `[Hardhat Compilation]: ${data.toString()}` |
||||
console.log('\x1b[32m%s\x1b[0m', msg) |
||||
result += msg + '\n' |
||||
}) |
||||
child.stderr.on('data', (err) => { |
||||
error += `[Hardhat Compilation]: ${err.toString()} \n` |
||||
}) |
||||
child.on('close', () => { |
||||
if (error && result) resolve(error + result) |
||||
else if (error) reject(error) |
||||
else resolve(result) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
checkPath() { |
||||
if (!fs.existsSync(this.buildPath)) { |
||||
this.listenOnHardHatFolder() |
||||
return false |
||||
} |
||||
return true |
||||
} |
||||
|
||||
private async processArtifact() { |
||||
console.log('processing artifact') |
||||
if (!this.checkPath()) return |
||||
// resolving the files
|
||||
const folderFiles = await fs.promises.readdir(this.buildPath) |
||||
const targetsSynced = [] |
||||
// name of folders are file names
|
||||
for (const file of folderFiles) { // ["artifacts/contracts/Greeter.sol/"]
|
||||
const contractFilePath = join(this.buildPath, file) |
||||
const stat = await fs.promises.stat(contractFilePath) |
||||
if (!stat.isDirectory()) continue |
||||
const files = await fs.promises.readdir(contractFilePath) |
||||
const compilationResult = { |
||||
input: {}, |
||||
output: { |
||||
contracts: {}, |
||||
sources: {} |
||||
}, |
||||
solcVersion: null, |
||||
target: null |
||||
} |
||||
for (const file of files) { |
||||
if (file.endsWith('.dbg.json')) { // "artifacts/contracts/Greeter.sol/Greeter.dbg.json"
|
||||
const stdFile = file.replace('.dbg.json', '.json') |
||||
const contentStd = await fs.promises.readFile(join(contractFilePath, stdFile), { encoding: 'utf-8' }) |
||||
const contentDbg = await fs.promises.readFile(join(contractFilePath, file), { encoding: 'utf-8' }) |
||||
const jsonDbg = JSON.parse(contentDbg) |
||||
const jsonStd = JSON.parse(contentStd) |
||||
compilationResult.target = jsonStd.sourceName |
||||
|
||||
targetsSynced.push(compilationResult.target) |
||||
const path = join(contractFilePath, jsonDbg.buildInfo) |
||||
const content = await fs.promises.readFile(path, { encoding: 'utf-8' }) |
||||
|
||||
await this.feedContractArtifactFile(content, compilationResult) |
||||
} |
||||
if (compilationResult.target) { |
||||
// we are only interested in the contracts that are in the target of the compilation
|
||||
compilationResult.output = { |
||||
...compilationResult.output, |
||||
contracts: { [compilationResult.target]: compilationResult.output.contracts[compilationResult.target] } |
||||
} |
||||
this.emit('compilationFinished', compilationResult.target, { sources: compilationResult.input }, 'soljson', compilationResult.output, compilationResult.solcVersion) |
||||
} |
||||
} |
||||
} |
||||
|
||||
clearTimeout(this.logTimeout) |
||||
this.logTimeout = setTimeout(() => { |
||||
this.call('terminal', 'log', { value: 'receiving compilation result from Hardhat. Select a file to populate the contract interaction interface.', type: 'log' }) |
||||
if (targetsSynced.length) { |
||||
console.log(`Processing artifacts for files: ${[...new Set(targetsSynced)].join(', ')}`) |
||||
// @ts-ignore
|
||||
this.call('terminal', 'log', { type: 'log', value: `synced with Hardhat: ${[...new Set(targetsSynced)].join(', ')}` }) |
||||
} else { |
||||
console.log('No artifacts to process') |
||||
// @ts-ignore
|
||||
this.call('terminal', 'log', { type: 'log', value: 'No artifacts from Hardhat to process' }) |
||||
} |
||||
}, 1000) |
||||
|
||||
} |
||||
|
||||
listenOnHardHatFolder() { |
||||
console.log('Hardhat artifacts folder doesn\'t exist... waiting for the compilation.') |
||||
try { |
||||
if (this.watcher) this.watcher.close() |
||||
this.watcher = chokidar.watch(this.currentSharedFolder, { depth: 2, ignorePermissionErrors: true, ignoreInitial: true }) |
||||
// watch for new folders
|
||||
this.watcher.on('addDir', (path: string) => { |
||||
console.log('add dir hardhat', path) |
||||
if (fs.existsSync(this.buildPath)) { |
||||
this.listenOnHardhatCompilation() |
||||
} |
||||
}) |
||||
} catch (e) { |
||||
console.log('listenOnHardHatFolder', e) |
||||
} |
||||
} |
||||
|
||||
async triggerProcessArtifact() { |
||||
console.log('triggerProcessArtifact') |
||||
// prevent multiple calls
|
||||
clearTimeout(this.processingTimeout) |
||||
this.processingTimeout = setTimeout(async () => await this.processArtifact(), 1000) |
||||
} |
||||
|
||||
listenOnHardhatCompilation() { |
||||
try { |
||||
console.log('listening on Hardhat compilation...', this.buildPath) |
||||
if (this.watcher) this.watcher.close() |
||||
this.watcher = chokidar.watch(this.buildPath, { depth: 1, ignorePermissionErrors: true, ignoreInitial: true }) |
||||
this.watcher.on('change', async () => await this.triggerProcessArtifact()) |
||||
this.watcher.on('add', async () => await this.triggerProcessArtifact()) |
||||
this.watcher.on('unlink', async () => await this.triggerProcessArtifact()) |
||||
// process the artifact on activation
|
||||
this.processArtifact() |
||||
} catch (e) { |
||||
console.log('listenOnHardhatCompilation', e) |
||||
} |
||||
} |
||||
|
||||
async sync() { |
||||
console.log('syncing from Hardhat') |
||||
this.processArtifact() |
||||
} |
||||
|
||||
async feedContractArtifactFile(artifactContent, compilationResultPart) { |
||||
const contentJSON = JSON.parse(artifactContent) |
||||
compilationResultPart.solcVersion = contentJSON.solcVersion |
||||
for (const file in contentJSON.input.sources) { |
||||
const source = contentJSON.input.sources[file] |
||||
const absPath = join(this.currentSharedFolder, file) |
||||
if (fs.existsSync(absPath)) { // if not that is a lib
|
||||
const contentOnDisk = await fs.promises.readFile(absPath, { encoding: 'utf-8' }) |
||||
if (contentOnDisk === source.content) { |
||||
compilationResultPart.input[file] = source |
||||
compilationResultPart.output['sources'][file] = contentJSON.output.sources[file] |
||||
compilationResultPart.output['contracts'][file] = contentJSON.output.contracts[file] |
||||
if (contentJSON.output.errors && contentJSON.output.errors.length) { |
||||
compilationResultPart.output['errors'] = contentJSON.output.errors.filter(error => error.sourceLocation.file === file) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,197 @@ |
||||
import { Profile } from "@remixproject/plugin-utils"; |
||||
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" |
||||
|
||||
import { ElectronBasePluginRemixdClient } from "../lib/remixd" |
||||
import * as utils from '../lib/utils' |
||||
import { existsSync, readdirSync, readFileSync, unlinkSync } from "fs-extra"; |
||||
|
||||
export interface OutputStandard { |
||||
description: string |
||||
title: string |
||||
confidence: string |
||||
severity: string |
||||
sourceMap: any |
||||
category?: string |
||||
reference?: string |
||||
example?: any |
||||
[key: string]: any |
||||
} |
||||
|
||||
const { spawn, execSync } = require('child_process') // eslint-disable-line
|
||||
const profile: Profile = { |
||||
name: 'slither', |
||||
displayName: 'electron slither', |
||||
description: 'electron slither', |
||||
} |
||||
|
||||
export class SlitherPlugin extends ElectronBasePlugin { |
||||
clients: any[] |
||||
constructor() { |
||||
super(profile, clientProfile, SlitherPluginClient) |
||||
this.methods = [...super.methods] |
||||
} |
||||
} |
||||
|
||||
const clientProfile: Profile = { |
||||
name: 'slither', |
||||
displayName: 'electron slither', |
||||
description: 'electron slither', |
||||
methods: ['analyse'] |
||||
} |
||||
|
||||
class SlitherPluginClient extends ElectronBasePluginRemixdClient { |
||||
|
||||
mapNpmDepsDir(list) { |
||||
const remixNpmDepsPath = utils.absolutePath('.deps/npm', this.currentSharedFolder) |
||||
const localNpmDepsPath = utils.absolutePath('node_modules', this.currentSharedFolder) |
||||
const npmDepsExists = existsSync(remixNpmDepsPath) |
||||
const nodeModulesExists = existsSync(localNpmDepsPath) |
||||
let isLocalDep = false |
||||
let isRemixDep = false |
||||
let allowPathString = '' |
||||
let remapString = '' |
||||
|
||||
for (const e of list) { |
||||
const importPath = e.replace(/import ['"]/g, '').trim() |
||||
const packageName = importPath.split('/')[0] |
||||
if (nodeModulesExists && readdirSync(localNpmDepsPath).includes(packageName)) { |
||||
isLocalDep = true |
||||
remapString += `${packageName}=./node_modules/${packageName} ` |
||||
} else if (npmDepsExists && readdirSync(remixNpmDepsPath).includes(packageName)) { |
||||
isRemixDep = true |
||||
remapString += `${packageName}=./.deps/npm/${packageName} ` |
||||
} |
||||
} |
||||
if (isLocalDep) allowPathString += './node_modules,' |
||||
if (isRemixDep) allowPathString += './.deps/npm,' |
||||
|
||||
return { remapString, allowPathString } |
||||
} |
||||
|
||||
transform(detectors: Record<string, any>[]): OutputStandard[] { |
||||
const standardReport: OutputStandard[] = [] |
||||
for (const e of detectors) { |
||||
const obj = {} as OutputStandard |
||||
obj.description = e.description |
||||
obj.title = e.check |
||||
obj.confidence = e.confidence |
||||
obj.severity = e.impact |
||||
obj.sourceMap = e.elements.map((element) => { |
||||
delete element.source_mapping.filename_used |
||||
delete element.source_mapping.filename_absolute |
||||
return element |
||||
}) |
||||
standardReport.push(obj) |
||||
} |
||||
return standardReport |
||||
} |
||||
|
||||
analyse(filePath: string, compilerConfig: Record<string, any>) { |
||||
return new Promise((resolve, reject) => { |
||||
const options = { cwd: this.currentSharedFolder, shell: true } |
||||
|
||||
const { currentVersion, optimize, evmVersion } = compilerConfig |
||||
if (currentVersion && currentVersion.includes('+commit')) { |
||||
// Get compiler version with commit id e.g: 0.8.2+commit.661d110
|
||||
const versionString: string = currentVersion.substring(0, currentVersion.indexOf('+commit') + 16) |
||||
this.log(`[Slither Analysis]: Compiler version is ${versionString}`) |
||||
let solcOutput: Buffer |
||||
// Check solc current installed version
|
||||
try { |
||||
solcOutput = execSync('solc --version', options) |
||||
} catch (err) { |
||||
this.error(err) |
||||
reject(new Error('Error in running solc command')) |
||||
} |
||||
if (!solcOutput.toString().includes(versionString)) { |
||||
this.log('[Slither Analysis]: Compiler version is different from installed solc version') |
||||
// Get compiler version without commit id e.g: 0.8.2
|
||||
const version: string = versionString.substring(0, versionString.indexOf('+commit')) |
||||
// List solc versions installed using solc-select
|
||||
try { |
||||
const solcSelectInstalledVersions: Buffer = execSync('solc-select versions', options) |
||||
// Check if required version is already installed
|
||||
if (!solcSelectInstalledVersions.toString().includes(version)) { |
||||
this.log(`[Slither Analysis]: Installing ${version} using solc-select`) |
||||
// Install required version
|
||||
execSync(`solc-select install ${version}`, options) |
||||
} |
||||
this.log(`[Slither Analysis]: Setting ${version} as current solc version using solc-select`) |
||||
// Set solc current version as required version
|
||||
execSync(`solc-select use ${version}`, options) |
||||
} catch (err) { |
||||
this.error(err) |
||||
reject(new Error('Error in running solc-select command')) |
||||
} |
||||
} else this.log('[Slither Analysis]: Compiler version is same as installed solc version') |
||||
} |
||||
// Allow paths and set solc remapping for import URLs
|
||||
const fileContent = readFileSync(utils.absolutePath(filePath, this.currentSharedFolder), 'utf8') |
||||
const importsArr = fileContent.match(/import ['"][^.|..](.+?)['"];/g) |
||||
let remaps = '' |
||||
if (importsArr?.length) { |
||||
const { remapString } = this.mapNpmDepsDir(importsArr) |
||||
remaps = remapString.trim() |
||||
} |
||||
const optimizeOption: string = optimize ? '--optimize' : '' |
||||
const evmOption: string = evmVersion ? `--evm-version ${evmVersion}` : '' |
||||
let solcArgs = '' |
||||
if (optimizeOption) { |
||||
solcArgs += optimizeOption + ' ' |
||||
} |
||||
if (evmOption) { |
||||
if (!solcArgs.endsWith(' ')) solcArgs += ' ' |
||||
solcArgs += evmOption |
||||
} |
||||
if (solcArgs) { |
||||
solcArgs = `--solc-args "${solcArgs.trimStart()}"` |
||||
} |
||||
const solcRemaps = remaps ? `--solc-remaps "${remaps}"` : '' |
||||
|
||||
const outputFile = 'remix-slither-report.json' |
||||
try { |
||||
// We don't keep the previous analysis
|
||||
const outputFilePath = utils.absolutePath(outputFile, this.currentSharedFolder) |
||||
if (existsSync(outputFilePath)) unlinkSync(outputFilePath) |
||||
} catch (e) { |
||||
this.error('unable to remove the output file') |
||||
this.error(e.message) |
||||
} |
||||
const cmd = `slither ${filePath} ${solcArgs} ${solcRemaps} --json ${outputFile}` |
||||
this.log('[Slither Analysis]: Running Slither...') |
||||
// Added `stdio: 'ignore'` as for contract with NPM imports analysis which is exported in 'stderr'
|
||||
// get too big and hangs the process. We process analysis from the report file only
|
||||
const child = spawn(cmd, { cwd: this.currentSharedFolder, shell: true, stdio: 'ignore' }) |
||||
|
||||
const response = {} |
||||
child.on('close', () => { |
||||
const outputFileAbsPath: string = utils.absolutePath(outputFile, this.currentSharedFolder) |
||||
// Check if slither report file exists
|
||||
if (existsSync(outputFileAbsPath)) { |
||||
let report = readFileSync(outputFileAbsPath, 'utf8') |
||||
report = JSON.parse(report) |
||||
if (report['success']) { |
||||
response['status'] = true |
||||
if (!report['results'] || !report['results'].detectors || !report['results'].detectors.length) { |
||||
response['count'] = 0 |
||||
} else { |
||||
const { detectors } = report['results'] |
||||
response['count'] = detectors.length |
||||
response['data'] = this.transform(detectors) |
||||
} |
||||
|
||||
resolve(response) |
||||
} else { |
||||
this.log(report['error']) |
||||
reject(new Error('Error in running Slither Analysis.')) |
||||
} |
||||
} else { |
||||
this.error('Error in generating Slither Analysis Report. Make sure Slither is properly installed.') |
||||
reject(new Error('Error in generating Slither Analysis Report. Make sure Slither is properly installed.')) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
|
@ -1,9 +0,0 @@ |
||||
export type branch = { |
||||
name: string |
||||
remote: remote |
||||
} |
||||
|
||||
export type remote = { |
||||
name: string |
||||
url: string |
||||
} |
@ -0,0 +1,51 @@ |
||||
import { screen } from 'electron'; |
||||
import { isPackaged, isE2E } from "../main"; |
||||
|
||||
|
||||
var MatomoTracker = require('matomo-tracker'); |
||||
|
||||
// Function to send events to Matomo
|
||||
export function trackEvent(category: string, action: string, name: string, value: string | number, new_visit: number = 0): void { |
||||
var matomo = new MatomoTracker(35, 'http://ethereumfoundation.matomo.cloud/matomo.php'); |
||||
matomo.on('error', function (err: any) { |
||||
console.log('error tracking request: ', err); |
||||
}); |
||||
|
||||
|
||||
// Customize the user agent
|
||||
const electronVersion = process.versions.electron; |
||||
const chromiumVersion = process.versions.chrome; |
||||
const os = process.platform; // 'darwin', 'win32', 'linux', etc.
|
||||
const osVersion = process.getSystemVersion(); |
||||
|
||||
const ua = `Electron/${electronVersion} (Chromium/${chromiumVersion}) ${os} ${osVersion}`; |
||||
|
||||
|
||||
const res = `${screen.getPrimaryDisplay().size.width}x${screen.getPrimaryDisplay().size.height}`; |
||||
|
||||
if ((process.env.NODE_ENV === 'production' || isPackaged) && !isE2E) { |
||||
console.log('trackEvent', category, action, name, value, ua, new_visit); |
||||
matomo.track({ |
||||
e_c: category, |
||||
e_a: action, |
||||
e_n: name, |
||||
e_v: value, |
||||
ua, |
||||
new_visit, |
||||
res, |
||||
url: 'https://github.com/remix-project-org/remix-desktop' |
||||
// You can add other parameters if needed
|
||||
}, (error: any) => { |
||||
if (error) { |
||||
console.error('Error tracking event:', error); |
||||
} else { |
||||
console.log('Event tracked successfully'); |
||||
} |
||||
}); |
||||
} else { |
||||
console.log('Matomo tracking is disabled'); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
@ -0,0 +1,2 @@ |
||||
{"openedFolders":["/home/bunsen/Documents/remix-reward"], |
||||
"recentFolders":["/home/bunsen/Documents/remix-reward"]} |
@ -0,0 +1,195 @@ |
||||
import { spawn, ChildProcess } from "child_process" |
||||
|
||||
export async function getBranches(path: string): Promise<string> { |
||||
return new Promise((resolve, reject) => { |
||||
const git = spawn('git', ['branch'], { cwd: path }) |
||||
let branches = '' |
||||
git.stdout.on('data', function (data) { |
||||
console.log('stdout git branches', data.toString()) |
||||
branches += data.toString() |
||||
}) |
||||
git.stderr.on('data', function (data) { |
||||
console.log('stderr git branches', data.toString()) |
||||
reject(data.toString()) |
||||
}) |
||||
git.on('close', function () { |
||||
resolve(branches) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
export async function getGitLog(path: string): Promise<string> { |
||||
return new Promise((resolve, reject) => { |
||||
const git = spawn('git', ['log'], { cwd: path }) |
||||
let logs = '' |
||||
git.stdout.on('data', function (data) { |
||||
logs += data.toString() |
||||
}) |
||||
git.stderr.on('err', function (data) { |
||||
reject(data.toString()) |
||||
}) |
||||
git.on('close', function () { |
||||
resolve(logs) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
export async function cloneOnServer(repo: string, path: string, name: string = 'bare') { |
||||
console.log('cloning', repo, path) |
||||
return new Promise((resolve, reject) => { |
||||
const git = spawn(`rm -rf ${name} && git`, ['clone', repo], { cwd: path, shell: true, detached: true }); |
||||
|
||||
git.stdout.on('data', function (data) { |
||||
console.log('stdout data cloning', data.toString()); |
||||
if (data.toString().includes('done')) { |
||||
resolve(git); |
||||
} |
||||
}); |
||||
|
||||
git.stderr.on('data', function (data) { |
||||
console.log('stderr data cloning', data.toString()); |
||||
if (data.toString().includes('into')) { |
||||
setTimeout(() => { |
||||
resolve(git); |
||||
}, 5000) |
||||
} |
||||
}); |
||||
|
||||
git.on('error', (error) => { |
||||
reject(`Process error: ${error.message}`); |
||||
}); |
||||
|
||||
git.on('exit', (code, signal) => { |
||||
if (code !== 0) { |
||||
reject(`Process exited with code: ${code} and signal: ${signal}`); |
||||
} |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
export async function onLocalGitRepoAddFile(path: string, file: string) { |
||||
console.log('adding file', file) |
||||
return new Promise((resolve, reject) => { |
||||
const git = spawn('touch', [file], { cwd: path }); |
||||
|
||||
git.stdout.on('data', function (data) { |
||||
console.log('stdout data adding file', data.toString()); |
||||
if (data.toString().includes('done')) { |
||||
resolve(git); |
||||
} |
||||
}); |
||||
|
||||
git.stderr.on('data', function (data) { |
||||
console.error('stderr adding file', data.toString()); |
||||
reject(data.toString()); |
||||
}); |
||||
|
||||
git.on('error', (error) => { |
||||
reject(`Process error: ${error.message}`); |
||||
}); |
||||
|
||||
git.on('exit', (code, signal) => { |
||||
if (code !== 0) { |
||||
reject(`Process exited with code: ${code} and signal: ${signal}`); |
||||
} else { |
||||
resolve(git); |
||||
} |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
export async function onLocalGitRepoPush(path: string, branch: string = 'master') { |
||||
console.log('pushing', path) |
||||
return new Promise((resolve, reject) => { |
||||
const git = spawn('git', ['push', 'origin', branch], { cwd: path, shell: true, detached: true }); |
||||
|
||||
git.stdout.on('data', function (data) { |
||||
console.log('stdout data pushing', data.toString()); |
||||
if (data.toString().includes('done')) { |
||||
resolve(git); |
||||
} |
||||
}); |
||||
|
||||
git.stderr.on('data', function (data) { |
||||
console.error('stderr data pushing', data.toString()); |
||||
if (data.toString().includes(branch)) { |
||||
resolve(git); |
||||
} |
||||
}); |
||||
|
||||
git.on('error', (error) => { |
||||
reject(`Process error: ${error.message}`); |
||||
}); |
||||
|
||||
git.on('exit', (code, signal) => { |
||||
if (code !== 0) { |
||||
reject(`Process exited with code: ${code} and signal: ${signal}`); |
||||
} else { |
||||
resolve(git); |
||||
} |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
|
||||
export async function createCommitOnLocalServer(path: string, message: string) { |
||||
console.log('committing', message, path) |
||||
return new Promise((resolve, reject) => { |
||||
const git = spawn('git add . && git', ['commit', '-m', message], { cwd: path, shell: true, detached: true }); |
||||
|
||||
git.stdout.on('data', function (data) { |
||||
console.log('data stdout committing', data.toString()); |
||||
if (data.toString().includes(message)) { |
||||
setTimeout(() => { |
||||
resolve(git); |
||||
}, 1000) |
||||
} |
||||
}); |
||||
|
||||
git.stderr.on('data', function (data) { |
||||
console.error('data commiting', data.toString()); |
||||
reject(data.toString()); |
||||
}); |
||||
|
||||
git.on('error', (error) => { |
||||
console.error('error', error); |
||||
reject(`Process error: ${error.message}`); |
||||
}); |
||||
|
||||
git.on('exit', (code, signal) => { |
||||
if (code !== 0) { |
||||
console.error('exit', code, signal); |
||||
reject(`Process exited with code: ${code} and signal: ${signal}`); |
||||
} else { |
||||
resolve(git); |
||||
} |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
|
||||
export async function spawnGitServer(path: string): Promise<ChildProcess> { |
||||
console.log(process.cwd()) |
||||
try { |
||||
const server = spawn('yarn && sh setup.sh && yarn start:server', [`${path}`], { cwd: process.cwd() + '/../remix-ide-e2e/src/githttpbackend/', shell: true, detached: true }) |
||||
console.log('spawned', server.stdout.closed, server.stderr.closed) |
||||
return new Promise((resolve, reject) => { |
||||
server.stdout.on('data', function (data) { |
||||
console.log(data.toString()) |
||||
if ( |
||||
data.toString().includes('is listening') |
||||
|| data.toString().includes('address already in use') |
||||
) { |
||||
console.log('resolving') |
||||
resolve(server) |
||||
} |
||||
}) |
||||
server.stderr.on('err', function (data) { |
||||
console.log(data.toString()) |
||||
reject(data.toString()) |
||||
}) |
||||
}) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
@ -0,0 +1,104 @@ |
||||
import os from 'os'; |
||||
import fs from 'fs'; |
||||
|
||||
|
||||
|
||||
const useIsoGit = process.argv.includes('--use-isogit'); |
||||
const useOffline = process.argv.includes('--use-offline'); |
||||
|
||||
// Function to read JSON file synchronously
|
||||
function readJSONFileSync(filename: string): any { |
||||
try { |
||||
const data = fs.readFileSync(filename, 'utf8'); |
||||
return JSON.parse(data); |
||||
} catch (err) { |
||||
throw err; |
||||
} |
||||
} |
||||
|
||||
const packageData: any = readJSONFileSync('package.json'); |
||||
const version = packageData.version; |
||||
|
||||
let channel: string = '' |
||||
|
||||
if (version.includes('beta')) { |
||||
channel = 'Beta'; |
||||
} else if (version.includes('alpha')) { |
||||
channel = 'Alpha'; |
||||
} else if (version.includes('insiders')) { |
||||
channel = 'Insiders'; |
||||
} |
||||
|
||||
// Determine if running on CircleCI or locally with --e2e-local
|
||||
const isLocalE2E = process.argv.includes('--e2e-local') && !process.env.CIRCLECI; |
||||
|
||||
module.exports = { |
||||
src_folders: ['build-e2e/remixdesktop/test/tests/app'], |
||||
output_folder: './reports/tests', |
||||
custom_commands_path: ['build-e2e/remix-ide-e2e/src/commands'], |
||||
page_objects_path: '', |
||||
globals_path: '', |
||||
test_settings: { |
||||
default: { |
||||
enable_fail_fast: true, |
||||
selenium_port: 4444, |
||||
selenium_host: 'localhost', |
||||
globals: { |
||||
waitForConditionTimeout: 10000, |
||||
asyncHookTimeout: 100000 |
||||
}, |
||||
screenshots: { |
||||
enabled: true, |
||||
path: './reports/screenshots', |
||||
on_failure: true, |
||||
on_error: true |
||||
}, |
||||
webdriver: { |
||||
start_process: true, |
||||
timeout_options: { |
||||
timeout: 60000, |
||||
retry_attempts: 3 |
||||
} |
||||
}, |
||||
desiredCapabilities: { |
||||
browserName: 'chrome', |
||||
javascriptEnabled: true, |
||||
acceptSslCerts: true, |
||||
'goog:chromeOptions': (() => { |
||||
const type = os.type(); |
||||
const arch = os.arch(); |
||||
let binaryPath = ""; |
||||
// Check if running on CircleCI or locally
|
||||
let args = process.env.CIRCLECI ? ["--e2e"] : ["--e2e-local"]; |
||||
|
||||
if(useIsoGit) args = [...args, '--use-isogit']; |
||||
if(useOffline) args = [...args, '--use-offline']; |
||||
|
||||
// Set display size
|
||||
const windowSize = "--window-size=1000,1000"; |
||||
args = [...args]; |
||||
|
||||
switch (type) { |
||||
case 'Windows_NT': |
||||
binaryPath = `./release/win-unpacked/Remix-Desktop-${channel}.exe`; |
||||
break; |
||||
case 'Darwin': |
||||
binaryPath = arch === 'x64' ?
|
||||
`release/mac/Remix-Desktop-${channel}.app/Contents/MacOS/Remix-Desktop-${channel}` : |
||||
`release/mac-arm64/Remix-Desktop-${channel}.app/Contents/MacOS/Remix-Desktop-${channel}`; |
||||
break; |
||||
case 'Linux': |
||||
binaryPath = "release/linux-unpacked/remixdesktop"; |
||||
break; |
||||
} |
||||
|
||||
console.log('binaryPath', binaryPath); |
||||
return { |
||||
binary: binaryPath, |
||||
args: args |
||||
}; |
||||
})() |
||||
} |
||||
} |
||||
} |
||||
}; |
@ -0,0 +1,37 @@ |
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
|
||||
|
||||
module.exports = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
'download compiler': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.clickLaunchIcon('solidity') |
||||
.pause(1000) |
||||
.setSolidityCompilerVersion('soljson-v0.8.23+commit.f704f362.js') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-id='selectedVersion' and contains(.,'0.8.23+commit.f704f362')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementContainsText('*[data-id="terminalJournal"]', 'Compiler downloaded from https://binaries.soliditylang.org/wasm/soljson-v0.8.23+commit.f704f362.js to soljson-v0.8.23+commit.f704f362.js', 10000) |
||||
.waitForElementPresent({ |
||||
selector: |
||||
"//a[@data-id='dropdown-item-soljson-v0.8.23+commit.f704f362.js']//*[contains(@class, 'fa-arrow-circle-down')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
|
||||
}, |
||||
'refresh': function (browser: NightwatchBrowser) { |
||||
browser.refresh() |
||||
.clickLaunchIcon('solidity') |
||||
.waitForElementVisible('*[data-id="versionSelector"]') |
||||
.click('*[data-id="versionSelector"]') |
||||
.waitForElementPresent({ |
||||
selector: |
||||
"//a[@data-id='dropdown-item-soljson-v0.8.23+commit.f704f362.js']//*[contains(@class, 'fa-arrow-circle-down')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,122 @@ |
||||
import {NightwatchBrowser} from 'nightwatch' |
||||
|
||||
const testsBash = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
open: function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[data-id="openFolderButton"]', 10000).click('*[data-id="openFolderButton"]') |
||||
}, |
||||
'open xterm linux and create a file': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="tabXTerm"]', 10000) |
||||
.click('*[data-id="tabXTerm"]') |
||||
.waitForElementVisible('*[data-type="remixUIXT"]', 10000) |
||||
.click('*[data-type="remixUIXT"]') |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('mkdir dir && cd dir && echo "test" >> example.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdir"]', 10000) |
||||
.openFile('dir') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdir/example.txt"]', 10000) |
||||
.openFile('dir/example.txt') |
||||
.getEditorValue((result) => { |
||||
browser.assert.equal(result, 'test\n') |
||||
}) |
||||
.waitForElementVisible('*[data-type="remixUIXT"]', 10000) |
||||
.click('*[data-type="remixUIXT"]') |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('echo "123" >> example.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.pause(1000) |
||||
.getEditorValue((result) => { |
||||
browser.assert.equal(result, 'test\n123\n') |
||||
}) |
||||
.setEditorValue('somethinginthere') |
||||
.pause(1000) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('cat example.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.pause(1000) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok((result.value as string).includes('somethinginthere')) |
||||
} |
||||
) |
||||
}, |
||||
} |
||||
|
||||
const testsWindows = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
open: function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[data-id="openFolderButton"]', 10000).click('*[data-id="openFolderButton"]') |
||||
}, |
||||
'open xterm window and create a file': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="tabXTerm"]', 10000) |
||||
.click('*[data-id="tabXTerm"]') |
||||
.waitForElementVisible('*[data-id="select_shell"]') |
||||
.click('*[data-id="select_shell"]') |
||||
.waitForElementVisible('*[data-id="select_powershell.exe"]') |
||||
.click('*[data-id="select_powershell.exe"]') |
||||
.pause(3000) |
||||
.waitForElementVisible("[data-active='1'][data-type='remixUIXT']", 10000) |
||||
.click("[data-active='1'][data-type='remixUIXT']") |
||||
.pause(1000) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('New-Item -ItemType Directory -Name "dir" ; Set-Location -Path "./dir" ; Add-Content -Path "example.txt" -Value "test" -Encoding UTF8').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdir"]', 10000) |
||||
.openFile('dir') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemdir/example.txt"]', 10000) |
||||
.openFile('dir/example.txt').pause(1000) |
||||
.getEditorValue((result) => { |
||||
browser.assert.equal(result, 'test\r\n') |
||||
}) |
||||
.pause(1000) |
||||
.waitForElementVisible("[data-active='1'][data-type='remixUIXT']", 10000) |
||||
.click("[data-active='1'][data-type='remixUIXT']") |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('Add-Content -Path "example.txt" -Value "123" -Encoding UTF8').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.pause(1000) |
||||
.getEditorValue((result) => { |
||||
browser.assert.equal(result, 'test\r\n123\r\n') |
||||
}) |
||||
.setEditorValue('somethinginthere') |
||||
.pause(1000) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('Get-Content example.txt').sendKeys(this.Keys.ENTER) |
||||
}).pause(1000) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok((result.value as string).includes('somethinginthere')) |
||||
} |
||||
) |
||||
} |
||||
} |
||||
|
||||
module.exports = { |
||||
...process.platform.startsWith('win')?testsWindows:testsBash |
||||
} |
@ -0,0 +1,157 @@ |
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
import { ChildProcess, spawn, execSync } from 'child_process' |
||||
import { homedir } from 'os' |
||||
import path from 'path' |
||||
import os from 'os' |
||||
|
||||
const projectDir = path.join('remix-desktop-test-' + Date.now().toString()) |
||||
const dir = '/tmp/' + projectDir |
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
installFoundry: function (browser: NightwatchBrowser) { |
||||
browser.perform(async (done) => { |
||||
await downloadFoundry() |
||||
await installFoundry() |
||||
await initFoundryProject() |
||||
done() |
||||
}) |
||||
}, |
||||
addScript: function (browser: NightwatchBrowser) { |
||||
// run script in console
|
||||
browser.executeAsync(function (dir, done) { |
||||
(window as any).electronAPI.openFolderInSameWindow(dir + '/hello_foundry/').then(done) |
||||
}, [dir], () => { |
||||
console.log('done window opened') |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewDivDraggableItemfoundry.toml"]', 10000) |
||||
}, |
||||
compile: function (browser: NightwatchBrowser) { |
||||
browser.perform(async (done) => { |
||||
console.log('generating compilation result') |
||||
await buildFoundryProject() |
||||
done() |
||||
}) |
||||
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Foundry').before(60000) |
||||
|
||||
let contractAaddress |
||||
browser.clickLaunchIcon('filePanel') |
||||
.openFile('src') |
||||
.openFile('src/Counter.sol') |
||||
.clickLaunchIcon('udapp') |
||||
.selectContract('Counter') |
||||
.createContract('') |
||||
.getAddressAtPosition(0, (address) => { |
||||
console.log(contractAaddress) |
||||
contractAaddress = address |
||||
}) |
||||
.clickInstance(0) |
||||
.clickFunction('increment - transact (not payable)') |
||||
.perform((done) => { |
||||
browser.testConstantFunction(contractAaddress, 'number - call', null, '0:\nuint256: 1').perform(() => { |
||||
done() |
||||
}) |
||||
}) |
||||
} |
||||
} |
||||
async function downloadFoundry(): Promise<void> { |
||||
console.log('downloadFoundry', process.cwd()) |
||||
try { |
||||
const server = spawn('curl -L https://foundry.paradigm.xyz | bash', [], { cwd: process.cwd(), shell: true, detached: true }) |
||||
return new Promise((resolve, reject) => { |
||||
server.stdout.on('data', function (data) { |
||||
console.log(data.toString()) |
||||
if ( |
||||
data.toString().includes("simply run 'foundryup' to install Foundry") |
||||
|| data.toString().includes("foundryup: could not detect shell, manually add") |
||||
) { |
||||
console.log('resolving') |
||||
resolve() |
||||
} |
||||
}) |
||||
server.stderr.on('err', function (data) { |
||||
console.log(data.toString()) |
||||
reject(data.toString()) |
||||
}) |
||||
}) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
async function installFoundry(): Promise<void> { |
||||
console.log('installFoundry', process.cwd()) |
||||
try { |
||||
const server = spawn('export PATH="' + homedir() + '/.foundry/bin:$PATH" && foundryup', [], { cwd: process.cwd(), shell: true, detached: true }) |
||||
return new Promise((resolve, reject) => { |
||||
server.stdout.on('data', function (data) { |
||||
console.log(data.toString()) |
||||
if ( |
||||
data.toString().includes("foundryup: done!") |
||||
) { |
||||
console.log('resolving') |
||||
resolve() |
||||
} |
||||
}) |
||||
server.stderr.on('err', function (data) { |
||||
console.log(data.toString()) |
||||
reject(data.toString()) |
||||
}) |
||||
}) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
async function initFoundryProject(): Promise<void> { |
||||
console.log('initFoundryProject', homedir()) |
||||
try { |
||||
if (process.env.CIRCLECI) { |
||||
spawn('git config --global user.email \"you@example.com\"', [], { cwd: homedir(), shell: true, detached: true }) |
||||
spawn('git config --global user.name \"Your Name\"', [], { cwd: homedir(), shell: true, detached: true }) |
||||
} |
||||
spawn('mkdir ' + projectDir, [], { cwd: '/tmp/', shell: true, detached: true }) |
||||
const server = spawn('export PATH="' + homedir() + '/.foundry/bin:$PATH" && forge init hello_foundry', [], { cwd: dir, shell: true, detached: true }) |
||||
server.stdout.pipe(process.stdout) |
||||
return new Promise((resolve, reject) => { |
||||
server.on('exit', function (exitCode) { |
||||
console.log("Child exited with code: " + exitCode); |
||||
console.log('end') |
||||
resolve() |
||||
}) |
||||
server.stderr.on('err', function (data) { |
||||
console.log('err', data.toString()) |
||||
}) |
||||
server.stdout.on('data', function (data) { |
||||
console.log('data', data.toString()) |
||||
}) |
||||
}) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
async function buildFoundryProject(): Promise<void> { |
||||
console.log('buildFoundryProject', homedir()) |
||||
try { |
||||
const server = spawn('export PATH="' + homedir() + '/.foundry/bin:$PATH" && forge build', [], { cwd: dir + '/hello_foundry', shell: true, detached: true }) |
||||
server.stdout.pipe(process.stdout) |
||||
return new Promise((resolve, reject) => { |
||||
server.on('exit', function (exitCode) { |
||||
console.log("Child exited with code: " + exitCode); |
||||
console.log('end') |
||||
resolve() |
||||
}) |
||||
}) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
module.exports = { |
||||
...{}//...process.platform.startsWith('linux') ? tests : {}
|
||||
} |
@ -0,0 +1,36 @@ |
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
|
||||
const gist_id = '02a847917a6a7ecaf4a7e0d4e68715bf' |
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
'start gist': function (browser: NightwatchBrowser) { |
||||
browser.end() |
||||
/* |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.waitForElementVisible('*[data-id="landingPageImportFromGist"]') |
||||
.click('*[data-id="landingPageImportFromGist"]') |
||||
.waitForElementVisible('*[data-id="gisthandlerModalDialogModalBody-react"] input[data-id="modalDialogCustomPromp"]') |
||||
.execute(function () { |
||||
(document.querySelector('*[data-id="gisthandlerModalDialogModalBody-react"] input[data-id="modalDialogCustomPromp"]') as any).focus() |
||||
}) |
||||
.setValue('*[data-id="gisthandlerModalDialogModalBody-react"] input[data-id="modalDialogCustomPromp"]', gist_id) |
||||
.modalFooterOKClick('gisthandler') |
||||
.pause(3000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewDivtreeViewItemREADME.txt"]') |
||||
}) |
||||
.click('[data-id="treeViewLitreeViewItemcontracts"]') |
||||
.openFile('contracts/3_Ballot.sol') |
||||
.end() |
||||
*/ |
||||
} |
||||
} |
||||
|
||||
module.exports = { |
||||
...tests |
||||
} |
@ -0,0 +1,203 @@ |
||||
import { ChildProcess, spawn } from "child_process" |
||||
import kill from 'tree-kill' |
||||
import { Nightwatch, NightwatchBrowser } from "nightwatch" |
||||
import { spawnGitServer, getGitLog, cloneOnServer, onLocalGitRepoAddFile, createCommitOnLocalServer, onLocalGitRepoPush, getBranches } from "../../lib/git" |
||||
let gitserver: ChildProcess |
||||
|
||||
/* |
||||
/ uses the git-http-backend package to create a git server ( if needed kill the server: kill -9 $(sudo lsof -t -i:6868) ) |
||||
/ GROUP 1: file operations PUSH PULL COMMIT SYNC FETCH CLONE ADD |
||||
/ GROUP 2: branch operations CREATE & PUBLISH |
||||
/ GROUP 3: file operations rename delete |
||||
*/ |
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
browser.hideToolTips() |
||||
done() |
||||
}, |
||||
after: function (browser: NightwatchBrowser) { |
||||
browser.perform((done) => { |
||||
console.log('kill server', gitserver.pid) |
||||
kill(gitserver.pid) |
||||
done() |
||||
}) |
||||
}, |
||||
|
||||
'run server #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser.perform(async (done) => { |
||||
gitserver = await spawnGitServer('/tmp/') |
||||
console.log('working directory', process.cwd()) |
||||
done() |
||||
}) |
||||
}, |
||||
|
||||
'clone a repo #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.pause(5000) |
||||
.waitForElementVisible('*[data-id="cloneButton"]') |
||||
.click('*[data-id="cloneButton"]') |
||||
.pause(1000) |
||||
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.click('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') |
||||
.setValue('*[data-id="modalDialogCustomPromptTextClone"]', 'http://localhost:6868/bare.git') |
||||
.click('[data-id="fileSystem-modal-footer-ok-react"]') |
||||
.pause(5000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') |
||||
.hideToolTips() |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]') |
||||
}, |
||||
'Update settings for git #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser. |
||||
clickLaunchIcon('dgit') |
||||
.waitForElementVisible('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.click('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.setValue('*[data-id="gitubUsername"]', 'git') |
||||
.pause(1000) |
||||
.setValue('*[data-id="githubEmail"]', 'git@example.com') |
||||
.pause(1000) |
||||
.click('*[data-id="saveGitHubCredentials"]') |
||||
.modalFooterOKClick('github-credentials-error') |
||||
|
||||
}, |
||||
|
||||
// GROUP 1
|
||||
|
||||
'check file added #group1 #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.addFile('test.txt', { content: 'hello world' }, 'README.md') |
||||
.clickLaunchIcon('dgit') |
||||
.pause(1000) |
||||
.click('*[data-id="sourcecontrol-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='new-untracked' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]') |
||||
.pause(1000) |
||||
.click('*[data-id="addToGitChangestest.txt"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='added-staged' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.setValue('*[data-id="commitMessage"]', 'testcommit') |
||||
.click('*[data-id="commitButton"]') |
||||
}, |
||||
'look at the commit #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="commits-panel"]') |
||||
.waitForElementPresent({ |
||||
selector: '//*[@data-id="commit-summary-testcommit-ahead"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'sync the commit #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="sourcecontrol-panel"]') |
||||
.click('*[data-id="sourcecontrol-panel"]') |
||||
|
||||
.waitForElementVisible('*[data-id="syncButton"]') |
||||
.click('*[data-id="syncButton"]') |
||||
.pause(2000) |
||||
.waitForElementVisible('*[data-id="commitButton"]') |
||||
.click('*[data-id="commits-panel"]') |
||||
.waitForElementPresent({ |
||||
selector: '//*[@data-id="commit-summary-testcommit-"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check the log #group1': async function (browser: NightwatchBrowser) { |
||||
const logs = await getGitLog('/tmp/git/bare.git') |
||||
console.log(logs) |
||||
browser.assert.ok(logs.includes('testcommit')) |
||||
}, |
||||
'change a file #group1': function (browser: NightwatchBrowser) { |
||||
browser. |
||||
openFile('test.txt'). |
||||
pause(1000). |
||||
setEditorValue('changes', null) |
||||
}, |
||||
'stage changed file #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.click('*[data-id="sourcecontrol-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='modified-unstaged' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]') |
||||
.click('*[data-id="addToGitChangestest.txt"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='modified-staged' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.setValue('*[data-id="commitMessage"]', 'testcommit2') |
||||
.click('*[data-id="commitButton"]') |
||||
}, |
||||
'push the commit #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="commands-panel"]') |
||||
.waitForElementVisible('*[data-id="sourcecontrol-push"]') |
||||
.click('*[data-id="sourcecontrol-push"]') |
||||
.pause(2000) |
||||
.click('*[data-id="commits-panel"]') |
||||
.waitForElementPresent({ |
||||
selector: '//*[@data-id="commit-summary-testcommit2-"]', |
||||
locateStrategy: 'xpath' |
||||
}).pause(2000) |
||||
}, |
||||
'check the log for testcommit2 #group1': async function (browser: NightwatchBrowser) { |
||||
const logs = await getGitLog('/tmp/git/bare.git') |
||||
console.log(logs) |
||||
browser.assert.ok(logs.includes('testcommit2')) |
||||
}, |
||||
'clone locally and add a file and push #group1': async function (browser: NightwatchBrowser) { |
||||
await cloneOnServer('http://localhost:6868/bare.git', '/tmp/') |
||||
await onLocalGitRepoAddFile('/tmp/bare/', 'test2.txt') |
||||
await createCommitOnLocalServer('/tmp/bare/', 'testlocal') |
||||
await onLocalGitRepoPush('/tmp/bare/', 'master') |
||||
}, |
||||
'run a git fetch #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(2000) |
||||
.click('*[data-id="commands-panel"]') |
||||
.waitForElementVisible('*[data-id="sourcecontrol-fetch-branch"]') |
||||
|
||||
.click('*[data-id="sourcecontrol-fetch-branch"]') |
||||
.pause(2000) |
||||
.click('*[data-id="commits-panel"]') |
||||
.waitForElementVisible('*[data-id="commits-panel-behind"]') |
||||
.click('*[data-id="commits-panel-behind"]') |
||||
.waitForElementPresent({ |
||||
selector: '//*[@data-id="commit-summary-testlocal-"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'run pull from the header #group1': function (browser: NightwatchBrowser) { |
||||
browser. |
||||
click('*[data-id="sourcecontrol-button-pull"]') |
||||
.waitForElementNotPresent('*[data-id="commits-panel-behind"]') |
||||
}, |
||||
'check if the file is added #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest2.txt"]') |
||||
}, |
||||
} |
||||
const useIsoGit = process.argv.includes('--use-isogit'); |
||||
if (process.platform.startsWith('win')) { |
||||
module.exports = {} |
||||
} |
||||
else
|
||||
module.exports = { ...tests } |
||||
|
||||
|
@ -0,0 +1,181 @@ |
||||
import { ChildProcess, spawn } from "child_process" |
||||
import kill from 'tree-kill' |
||||
import { Nightwatch, NightwatchBrowser } from "nightwatch" |
||||
import { spawnGitServer, getGitLog, cloneOnServer, onLocalGitRepoAddFile, createCommitOnLocalServer, onLocalGitRepoPush, getBranches } from "../../lib/git" |
||||
let gitserver: ChildProcess |
||||
|
||||
/* |
||||
/ uses the git-http-backend package to create a git server ( if needed kill the server: kill -9 $(sudo lsof -t -i:6868) ) |
||||
/ GROUP 1: file operations PUSH PULL COMMIT SYNC FETCH CLONE ADD |
||||
/ GROUP 2: branch operations CREATE & PUBLISH |
||||
/ GROUP 3: file operations rename delete |
||||
*/ |
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
browser.hideToolTips() |
||||
done() |
||||
}, |
||||
after: function (browser: NightwatchBrowser) { |
||||
browser.perform((done) => { |
||||
console.log('kill server', gitserver.pid) |
||||
kill(gitserver.pid) |
||||
done() |
||||
}) |
||||
}, |
||||
|
||||
'run server #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser.perform(async (done) => { |
||||
gitserver = await spawnGitServer('/tmp/') |
||||
console.log('working directory', process.cwd()) |
||||
done() |
||||
}) |
||||
}, |
||||
|
||||
'clone a repo #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.pause(5000) |
||||
.waitForElementVisible('*[data-id="cloneButton"]') |
||||
.click('*[data-id="cloneButton"]') |
||||
.pause(1000) |
||||
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.click('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') |
||||
.setValue('*[data-id="modalDialogCustomPromptTextClone"]', 'http://localhost:6868/bare.git') |
||||
.click('[data-id="fileSystem-modal-footer-ok-react"]') |
||||
.pause(5000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') |
||||
.hideToolTips() |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]') |
||||
}, |
||||
'Update settings for git #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser. |
||||
clickLaunchIcon('dgit') |
||||
.waitForElementVisible('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.click('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.setValue('*[data-id="gitubUsername"]', 'git') |
||||
.pause(1000) |
||||
.setValue('*[data-id="githubEmail"]', 'git@example.com') |
||||
.pause(1000) |
||||
.click('*[data-id="saveGitHubCredentials"]') |
||||
.modalFooterOKClick('github-credentials-error') |
||||
|
||||
}, |
||||
|
||||
// GROUP 2
|
||||
'create a branch #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="branches-panel"]') |
||||
.waitForElementVisible('*[data-id="newbranchname"]') |
||||
.setValue('*[data-id="newbranchname"]', 'testbranch') |
||||
.click('*[data-id="sourcecontrol-create-branch"]') |
||||
.waitForElementVisible('*[data-id="branches-current-branch-testbranch"]') |
||||
.pause(1000) |
||||
}, |
||||
'check if the branch is in the filePanel #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="workspaceGitBranchesDropdown"]') |
||||
.click('[data-id="workspaceGitBranchesDropdown"]') |
||||
.waitForElementVisible('*[data-id="workspaceGit-testbranch"]') |
||||
.expect.element('[data-id="workspaceGit-testbranch"]').text.to.contain('✓ ') |
||||
}, |
||||
'publish the branch #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.waitForElementVisible('*[data-id="sourcecontrol-panel"]') |
||||
.click('*[data-id="sourcecontrol-panel"]') |
||||
.pause(1000) |
||||
.click('*[data-id="publishBranchButton"]') |
||||
.pause(2000) |
||||
.waitForElementNotVisible('*[data-id="publishBranchButton"]') |
||||
}, |
||||
'check if the branch is published #group2': async function (browser: NightwatchBrowser) { |
||||
const branches = await getBranches('/tmp/git/bare.git') |
||||
browser.assert.ok(branches.includes('testbranch')) |
||||
}, |
||||
'add file to new branch #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.addFile('test.txt', { content: 'hello world' }, 'README.md') |
||||
.clickLaunchIcon('dgit') |
||||
.pause(2000) |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='new-untracked' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]') |
||||
.pause(1000) |
||||
.click('*[data-id="addToGitChangestest.txt"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='added-staged' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.setValue('*[data-id="commitMessage"]', 'testcommit') |
||||
.click('*[data-id="commitButton"]') |
||||
.pause(1000) |
||||
}, |
||||
'check if the commit is ahead in the branches list #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="branches-panel"]') |
||||
.click('*[data-id="branches-panel"]') |
||||
.waitForElementVisible('*[data-id="branches-current-branch-testbranch"]') |
||||
.click({ |
||||
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branches-current-branch-testbranch']", |
||||
locateStrategy: 'xpath', |
||||
suppressNotFoundErrors: true |
||||
}) |
||||
.click({ |
||||
selector: "//*[@data-id='branches-panel-content']//*[@data-id='commits-panel-ahead']", |
||||
locateStrategy: 'xpath', |
||||
suppressNotFoundErrors: true |
||||
}) |
||||
.click({ |
||||
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branchdifference-commits-testbranch-ahead']//*[@data-id='commit-summary-testcommit-ahead']", |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.click({ |
||||
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branchdifference-commits-testbranch-ahead']//*[@data-id='commit-change-added-test.txt']", |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.click({ |
||||
selector: "//*[@data-id='branches-panel-content']//*[@data-id='local-branch-commits-testbranch']//*[@data-id='commit-summary-testcommit-ahead']", |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-id='branches-panel-content']//*[@data-id='local-branch-commits-testbranch']//*[@data-id='commit-change-added-test.txt']", |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
}, |
||||
'switch back to master #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click({ |
||||
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branches-toggle-branch-master']", |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branches-toggle-current-branch-master']", |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
}, |
||||
'check if test file is gone #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.txt"]') |
||||
} |
||||
} |
||||
|
||||
const useIsoGit = process.argv.includes('--useIsoGit'); |
||||
if (process.platform.startsWith('win')) { |
||||
module.exports = {} |
||||
} |
||||
else
|
||||
module.exports = { ...tests } |
@ -0,0 +1,153 @@ |
||||
import { ChildProcess, spawn } from "child_process" |
||||
import kill from 'tree-kill' |
||||
import { Nightwatch, NightwatchBrowser } from "nightwatch" |
||||
import { spawnGitServer, getGitLog, cloneOnServer, onLocalGitRepoAddFile, createCommitOnLocalServer, onLocalGitRepoPush, getBranches } from "../../lib/git" |
||||
let gitserver: ChildProcess |
||||
|
||||
/* |
||||
/ uses the git-http-backend package to create a git server ( if needed kill the server: kill -9 $(sudo lsof -t -i:6868) ) |
||||
/ GROUP 1: file operations PUSH PULL COMMIT SYNC FETCH CLONE ADD |
||||
/ GROUP 2: branch operations CREATE & PUBLISH |
||||
/ GROUP 3: file operations rename delete |
||||
*/ |
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
browser.hideToolTips() |
||||
done() |
||||
}, |
||||
after: function (browser: NightwatchBrowser) { |
||||
browser.perform((done) => { |
||||
console.log('kill server', gitserver.pid) |
||||
kill(gitserver.pid) |
||||
done() |
||||
}) |
||||
}, |
||||
|
||||
'run server #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser.perform(async (done) => { |
||||
gitserver = await spawnGitServer('/tmp/') |
||||
console.log('working directory', process.cwd()) |
||||
done() |
||||
}) |
||||
}, |
||||
|
||||
'clone a repo #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.pause(5000) |
||||
.waitForElementVisible('*[data-id="cloneButton"]') |
||||
.click('*[data-id="cloneButton"]') |
||||
.pause(1000) |
||||
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.click('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') |
||||
.setValue('*[data-id="modalDialogCustomPromptTextClone"]', 'http://localhost:6868/bare.git') |
||||
.click('[data-id="fileSystem-modal-footer-ok-react"]') |
||||
.pause(5000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') |
||||
.hideToolTips() |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]') |
||||
}, |
||||
'Update settings for git #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser. |
||||
clickLaunchIcon('dgit') |
||||
.saveScreenshot('./reports/screenshots/gitui.png') |
||||
.waitForElementVisible('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.saveScreenshot('./reports/screenshots/gitui2.png') |
||||
.pause(1000) |
||||
.saveScreenshot('./reports/screenshots/gitui3.png') |
||||
.click('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.setValue('*[data-id="gitubUsername"]', 'git') |
||||
.pause(1000) |
||||
.setValue('*[data-id="githubEmail"]', 'git@example.com') |
||||
.pause(1000) |
||||
.click('*[data-id="saveGitHubCredentials"]') |
||||
.pause(1000) |
||||
.modalFooterOKClick('github-credentials-error') |
||||
|
||||
}, |
||||
|
||||
'check file added #group1 #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.addFile('test.txt', { content: 'hello world' }, 'README.md') |
||||
.clickLaunchIcon('dgit') |
||||
.pause(1000) |
||||
.click('*[data-id="sourcecontrol-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='new-untracked' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]') |
||||
.pause(1000) |
||||
.click('*[data-id="addToGitChangestest.txt"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='added-staged' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.setValue('*[data-id="commitMessage"]', 'testcommit') |
||||
.click('*[data-id="commitButton"]') |
||||
}, |
||||
|
||||
// group 3
|
||||
'rename a file #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.txt"]') |
||||
.click('*[data-id="treeViewLitreeViewItemtest.txt"]') |
||||
.renamePath('test.txt', 'test_rename', 'test_rename.txt') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest_rename.txt"]') |
||||
.pause(1000) |
||||
}, |
||||
'stage renamed file #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='deleted-unstaged' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='new-untracked' and @data-file='/test_rename.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.pause(2000) |
||||
.click('*[data-id="sourcecontrol-add-all"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='added-staged' and @data-file='/test_rename.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'undo the rename #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="unStageStagedtest_rename.txt"]') |
||||
.click('*[data-id="unStageStagedtest_rename.txt"]') |
||||
.pause(1000) |
||||
.click('*[data-id="unDoStagedtest.txt"]') |
||||
.pause(1000) |
||||
.waitForElementNotPresent({ |
||||
selector: "//*[@data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check if file is returned #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.txt"]') |
||||
}, |
||||
|
||||
|
||||
} |
||||
|
||||
const useIsoGit = process.argv.includes('--use-isogit'); |
||||
if (process.platform.startsWith('win')) { |
||||
module.exports = {} |
||||
} |
||||
else
|
||||
module.exports = { ...tests } |
@ -0,0 +1,200 @@ |
||||
import { ChildProcess, spawn } from "child_process" |
||||
import kill from 'tree-kill' |
||||
import { Nightwatch, NightwatchBrowser } from "nightwatch" |
||||
import { spawnGitServer, getGitLog, cloneOnServer, onLocalGitRepoAddFile, createCommitOnLocalServer, onLocalGitRepoPush, getBranches } from "../../lib/git" |
||||
let gitserver: ChildProcess |
||||
|
||||
/* |
||||
/ uses the git-http-backend package to create a git server ( if needed kill the server: kill -9 $(sudo lsof -t -i:6868) ) |
||||
/ GROUP 1: file operations PUSH PULL COMMIT SYNC FETCH CLONE ADD |
||||
/ GROUP 2: branch operations CREATE & PUBLISH |
||||
/ GROUP 3: file operations rename delete |
||||
*/ |
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
browser.hideToolTips() |
||||
done() |
||||
}, |
||||
after: function (browser: NightwatchBrowser) { |
||||
browser.perform((done) => { |
||||
console.log('kill server', gitserver.pid) |
||||
kill(gitserver.pid) |
||||
done() |
||||
}) |
||||
}, |
||||
|
||||
'run server #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser.perform(async (done) => { |
||||
gitserver = await spawnGitServer('/tmp/') |
||||
console.log('working directory', process.cwd()) |
||||
done() |
||||
}) |
||||
}, |
||||
|
||||
'clone a repo #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.pause(5000) |
||||
.waitForElementVisible('*[data-id="cloneButton"]') |
||||
.click('*[data-id="cloneButton"]') |
||||
.pause(1000) |
||||
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.click('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') |
||||
.setValue('*[data-id="modalDialogCustomPromptTextClone"]', 'http://localhost:6868/bare.git') |
||||
.click('[data-id="fileSystem-modal-footer-ok-react"]') |
||||
.pause(5000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') |
||||
.hideToolTips() |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]') |
||||
}, |
||||
'Update settings for git #group1 #group2 #group3': function (browser: NightwatchBrowser) { |
||||
browser. |
||||
clickLaunchIcon('dgit') |
||||
.waitForElementVisible('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.click('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.setValue('*[data-id="gitubUsername"]', 'git') |
||||
.pause(1000) |
||||
.setValue('*[data-id="githubEmail"]', 'git@example.com') |
||||
.pause(1000) |
||||
.click('*[data-id="saveGitHubCredentials"]') |
||||
.pause(1000) |
||||
.modalFooterOKClick('github-credentials-error') |
||||
|
||||
}, |
||||
|
||||
// GROUP 1
|
||||
|
||||
'check file added #group1 #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.addFile('test.txt', { content: 'hello world' }, 'README.md') |
||||
.clickLaunchIcon('dgit') |
||||
.pause(1000) |
||||
.click('*[data-id="sourcecontrol-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='new-untracked' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]') |
||||
.pause(1000) |
||||
.click('*[data-id="addToGitChangestest.txt"]') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-status='added-staged' and @data-file='/test.txt']", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.setValue('*[data-id="commitMessage"]', 'testcommit') |
||||
.click('*[data-id="commitButton"]') |
||||
}, |
||||
'look at the commit #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="commits-panel"]') |
||||
.waitForElementPresent({ |
||||
selector: '//*[@data-id="commit-summary-testcommit-ahead"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'add second remote #group4': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.click('*[data-id="remotes-panel"]') |
||||
.waitForElementVisible('*[data-id="add-manual-remoteurl"]') |
||||
.setValue('*[data-id="add-manual-remoteurl"]', 'http://localhost:6868/bare2.git') |
||||
.waitForElementVisible('*[data-id="add-manual-remotename"]') |
||||
.setValue('*[data-id="add-manual-remotename"]', 'origin2') |
||||
.waitForElementVisible('*[data-id="add-manual-remotebtn"]') |
||||
.click('*[data-id="add-manual-remotebtn"]') |
||||
}, |
||||
'check the buttons #group4': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="default-remote-check-origin"]') |
||||
.waitForElementVisible('*[data-id="set-as-default-origin2"]') |
||||
}, |
||||
'check the commands #group4': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="commands-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'switch to origin2 #group4': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="remotes-panel"]') |
||||
.waitForElementVisible('*[data-id="set-as-default-origin2"]') |
||||
.click('*[data-id="set-as-default-origin2"]') |
||||
}, |
||||
'check the commands for origin2 #group4': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="commands-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin2')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'sync the commit #group4': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="sourcecontrol-panel"]') |
||||
.click('*[data-id="sourcecontrol-panel"]') |
||||
.waitForElementVisible('*[data-id="syncButton"]') |
||||
.click('*[data-id="syncButton"]') |
||||
.waitForElementVisible('*[data-id="commitButton"]') |
||||
.click('*[data-id="commits-panel"]') |
||||
.waitForElementPresent({ |
||||
selector: '//*[@data-id="commit-summary-testcommit-"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check the log #group4': async function (browser: NightwatchBrowser) { |
||||
const logs = await getGitLog('/tmp/git/bare2.git') |
||||
console.log(logs) |
||||
browser.assert.ok(logs.includes('testcommit')) |
||||
const logs2 = await getGitLog('/tmp/git/bare.git') |
||||
console.log(logs2) |
||||
console.log(logs2.includes('testcommit3')) |
||||
browser.assert.ok(logs2.includes('testcommit3')) |
||||
}, |
||||
'switch to origin #group4': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(5000) |
||||
.click('*[data-id="remotes-panel"]') |
||||
.waitForElementVisible('*[data-id="set-as-default-origin"]') |
||||
.pause(1000) |
||||
.click('*[data-id="set-as-default-origin"]') |
||||
}, |
||||
'check the commands for origin #group4': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="commands-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check the commit ahead #group4': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="sourcecontrol-panel"]') |
||||
.click('*[data-id="sourcecontrol-panel"]') |
||||
.waitForElementVisible('*[data-id="syncButton"]') |
||||
// do not sync
|
||||
.click('*[data-id="commits-panel"]') |
||||
.waitForElementPresent({ |
||||
selector: '//*[@data-id="commit-summary-testcommit-ahead"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
} |
||||
|
||||
const useIsoGit = process.argv.includes('--use-isogit'); |
||||
if (process.platform.startsWith('win')) { |
||||
module.exports = {} |
||||
} |
||||
else
|
||||
module.exports = { ...tests } |
@ -0,0 +1,28 @@ |
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
|
||||
|
||||
module.exports = { |
||||
'@isogit': true, |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
'clone a repo': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.waitForElementVisible('button[data-id="landingPageImportFromGit"]') |
||||
.click('button[data-id="landingPageImportFromGit"]') |
||||
.pause(1000) |
||||
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.click('[data-id="fileSystemModalDialogModalBody-react"]') |
||||
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') |
||||
.setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') |
||||
.click('[data-id="fileSystem-modal-footer-ok-react"]') |
||||
.pause(3000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') |
||||
}) |
||||
.end() |
||||
} |
||||
} |
@ -0,0 +1,255 @@ |
||||
import { NightwatchBrowser } from "nightwatch" |
||||
|
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
browser.hideToolTips() |
||||
done() |
||||
}, |
||||
|
||||
'open default template': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]') |
||||
.click('button[data-id="landingPageImportFromTemplate"]') |
||||
.waitForElementPresent('*[data-id="create-remixDefault"]') |
||||
.scrollAndClick('*[data-id="create-remixDefault"]') |
||||
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') |
||||
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.pause(3000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') |
||||
}) |
||||
|
||||
}, |
||||
'Update settings for git #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="initgit-btn"]') |
||||
.click('*[data-id="initgit-btn"]') |
||||
.waitForElementNotPresent('*[data-id="initgit-btn"]') |
||||
}, |
||||
'launch github login via FE #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="filepanel-login-github"]') |
||||
.click('*[data-id="filepanel-login-github"]') |
||||
}, |
||||
'login to github #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="github-panel"]') |
||||
.waitForElementVisible('*[data-id="gitubUsername"]') |
||||
.setValue('*[data-id="githubToken"]', process.env.dgit_token) |
||||
.pause(1000) |
||||
.setValue('*[data-id="gitubUsername"]', 'git') |
||||
.pause(1000) |
||||
.setValue('*[data-id="githubEmail"]', 'git@example.com') |
||||
.pause(1000) |
||||
.click('*[data-id="saveGitHubCredentials"]') |
||||
}, |
||||
'check if the settings are loaded #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="connected-as-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="connected-img-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="connected-link-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="remotes-panel"]') |
||||
}, |
||||
'check the FE for the auth user #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.waitForElementVisible('*[data-id="filepanel-connected-img-bunsenstraat"]') |
||||
}, |
||||
'clone a repository #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.click('*[data-id="clone-panel"]') |
||||
.click({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[@data-id="fetch-repositories"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[@id="repository-select"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[@id="repository-select"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[contains(text(), "awesome-remix")]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[contains(text(), "awesome-remix")]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[@id="branch-select"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[@id="branch-select"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[contains(text(), "master")]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[@data-id="clonebtn-ethereum/awesome-remix-master"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="clone-panel-content"]//*[@data-id="clonebtn-ethereum/awesome-remix-master"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.pause(5000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[2]) |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') |
||||
}) |
||||
}, |
||||
'check if there is a README.md file #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]') |
||||
}, |
||||
'check the commands panel #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.click('*[data-id="commands-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'master')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'master')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check the remotes #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
|
||||
.click('*[data-id="remotes-panel"]') |
||||
.waitForElementVisible('*[data-id="remotes-panel-content"]') |
||||
.pause(2000) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-detail-origin-default"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="branches-current-branch-master"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-sync-origin"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="branches-branch-links"]', |
||||
locateStrategy: 'xpath', |
||||
timeout: 10000 |
||||
}) |
||||
|
||||
}, |
||||
'check the commits of branch links #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="branches-branch-links"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="branches-branch-links"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="commit-summary-linking fixed-"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'switch to branch links #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="branches-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-id="branches-branch-links"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.pause(1000) |
||||
.click({ |
||||
selector: '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-id="branches-toggle-branch-links"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-id="branches-toggle-current-branch-links"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check the local branches #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="branches-panel-content-local-branches"]//*[@data-id="branches-toggle-current-branch-links"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check the local commits #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="commits-panel"]') |
||||
.pause(1000) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="commits-current-branch-links"]//*[@data-id="commit-summary-linking fixed-"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="commits-current-branch-links"]//*[@data-id="commit-summary-linking fixed-"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="commits-current-branch-links"]//*[@data-id="commit-change-modified-README.md"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check the commands panel for links #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('*[data-id="commands-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'links')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'links')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'disconnect github #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.click('*[data-id="github-panel"]') |
||||
.waitForElementVisible('*[data-id="disconnect-github"]') |
||||
.pause(1000) |
||||
.click('*[data-id="disconnect-github"]') |
||||
.waitForElementNotPresent('*[data-id="connected-as-bunsenstraat"]') |
||||
}, |
||||
'check the FE for the disconnected auth user #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.waitForElementNotPresent('*[data-id="filepanel-connected-img-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="filepanel-login-github"]') |
||||
}, |
||||
} |
||||
|
||||
module.exports = tests |
@ -0,0 +1,190 @@ |
||||
import { NightwatchBrowser } from "nightwatch" |
||||
|
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
browser.hideToolTips() |
||||
done() |
||||
}, |
||||
|
||||
'open default template': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]') |
||||
.click('button[data-id="landingPageImportFromTemplate"]') |
||||
.waitForElementPresent('*[data-id="create-remixDefault"]') |
||||
.scrollAndClick('*[data-id="create-remixDefault"]') |
||||
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') |
||||
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.pause(3000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') |
||||
}) |
||||
|
||||
}, |
||||
'Update settings for git #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="initgit-btn"]') |
||||
.click('*[data-id="initgit-btn"]') |
||||
.waitForElementNotPresent('*[data-id="initgit-btn"]') |
||||
}, |
||||
'launch github login via FE #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="filepanel-login-github"]') |
||||
.click('*[data-id="filepanel-login-github"]') |
||||
}, |
||||
'login to github #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="github-panel"]') |
||||
.waitForElementVisible('*[data-id="gitubUsername"]') |
||||
.setValue('*[data-id="githubToken"]', process.env.dgit_token) |
||||
.pause(1000) |
||||
.setValue('*[data-id="gitubUsername"]', 'git') |
||||
.pause(1000) |
||||
.setValue('*[data-id="githubEmail"]', 'git@example.com') |
||||
.pause(1000) |
||||
.click('*[data-id="saveGitHubCredentials"]') |
||||
}, |
||||
'check if the settings are loaded #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="connected-as-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="connected-img-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="connected-link-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="remotes-panel"]') |
||||
}, |
||||
'check the FE for the auth user #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.waitForElementVisible('*[data-id="filepanel-connected-img-bunsenstraat"]') |
||||
}, |
||||
'add a remote #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.clickLaunchIcon('dgit') |
||||
.waitForElementVisible('*[data-id="remotes-panel"]') |
||||
.click('*[data-id="remotes-panel"]') |
||||
.click({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="fetch-repositories"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@id="repository-select"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@id="repository-select"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[contains(text(), "awesome-remix")]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[contains(text(), "awesome-remix")]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-panel-remotename"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.setValue({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-panel-remotename"]', |
||||
locateStrategy: 'xpath' |
||||
}, 'newremote') |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-panel-addremote"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.click({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-panel-addremote"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-detail-newremote-default"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check the commands panel for newremote #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.click('*[data-id="commands-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'newremote')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.pause(1000) |
||||
.getAttribute({ |
||||
selector: '//*[@data-id="sourcecontrol-pull"]', |
||||
locateStrategy: 'xpath' |
||||
}, 'disabled', (result) => { |
||||
if (result.value) { |
||||
browser.assert.fail('Button is disabled') |
||||
} else { |
||||
browser.assert.ok(true) |
||||
} |
||||
}) |
||||
}, |
||||
'remove the remote #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.click('*[data-id="remotes-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-rm-newremote"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.pause(2000) |
||||
.click({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-rm-newremote"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.pause(1000) |
||||
.waitForElementNotPresent({ |
||||
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-detail-newremote-default"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
}, |
||||
'check the commands panel for removed remote #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.click('*[data-id="commands-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-remote-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementNotPresent({ |
||||
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'newremote')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]", |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.getAttribute({ |
||||
selector: '//*[@data-id="sourcecontrol-pull"]', |
||||
locateStrategy: 'xpath' |
||||
}, 'disabled', (result) => { |
||||
if (result.value) { |
||||
browser.assert.ok(true) |
||||
} else { |
||||
browser.assert.fail('Button is not disabled') |
||||
} |
||||
}) |
||||
}, |
||||
} |
||||
|
||||
module.exports = tests |
@ -0,0 +1,180 @@ |
||||
import { NightwatchBrowser } from "nightwatch" |
||||
|
||||
const useIsoGit = process.argv.includes('--use-isogit'); |
||||
let commitCount = 0 |
||||
let branchCount = 0 |
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
browser.hideToolTips() |
||||
done() |
||||
}, |
||||
|
||||
'open default template': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]') |
||||
.click('button[data-id="landingPageImportFromTemplate"]') |
||||
.waitForElementPresent('*[data-id="create-remixDefault"]') |
||||
.scrollAndClick('*[data-id="create-remixDefault"]') |
||||
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') |
||||
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.pause(3000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') |
||||
}) |
||||
|
||||
}, |
||||
'Update settings for git #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="initgit-btn"]') |
||||
.click('*[data-id="initgit-btn"]') |
||||
.waitForElementNotPresent('*[data-id="initgit-btn"]') |
||||
}, |
||||
'launch github login via FE #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="filepanel-login-github"]') |
||||
.click('*[data-id="filepanel-login-github"]') |
||||
}, |
||||
'login to github #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="github-panel"]') |
||||
.waitForElementVisible('*[data-id="gitubUsername"]') |
||||
.setValue('*[data-id="githubToken"]', process.env.dgit_token) |
||||
.pause(1000) |
||||
.setValue('*[data-id="gitubUsername"]', 'git') |
||||
.pause(1000) |
||||
.setValue('*[data-id="githubEmail"]', 'git@example.com') |
||||
.pause(1000) |
||||
.click('*[data-id="saveGitHubCredentials"]') |
||||
}, |
||||
'check if the settings are loaded #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="connected-as-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="connected-img-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="connected-link-bunsenstraat"]') |
||||
.waitForElementVisible('*[data-id="remotes-panel"]') |
||||
}, |
||||
'check the FE for the auth user #group1 #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('filePanel') |
||||
.waitForElementVisible('*[data-id="filepanel-connected-img-bunsenstraat"]') |
||||
}, |
||||
// pagination test
|
||||
'clone repo #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('dgit') |
||||
.waitForElementVisible('*[data-id="clone-panel"]') |
||||
.click('*[data-id="clone-panel"]') |
||||
.waitForElementVisible('*[data-id="clone-url"]') |
||||
.setValue('*[data-id="clone-url"]', 'https://github.com/yann300/remix-reward') |
||||
.waitForElementVisible('*[data-id="clone-branch"]') |
||||
.setValue('*[data-id="clone-branch"]', 'master') |
||||
.waitForElementVisible('*[data-id="clone-btn"]') |
||||
.click('*[data-id="clone-btn"]') |
||||
.clickLaunchIcon('filePanel') |
||||
.pause(5000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[2]) |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') |
||||
}) |
||||
}, |
||||
'Update settings for git #group3': function (browser: NightwatchBrowser) { |
||||
browser. |
||||
clickLaunchIcon('dgit') |
||||
.waitForElementVisible('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.click('*[data-id="github-panel"]') |
||||
.pause(1000) |
||||
.setValue('*[data-id="githubToken"]', 'invalidtoken') |
||||
.pause(1000) |
||||
.setValue('*[data-id="gitubUsername"]', 'git') |
||||
.pause(1000) |
||||
.setValue('*[data-id="githubEmail"]', 'git@example.com') |
||||
.pause(1000) |
||||
.click('*[data-id="saveGitHubCredentials"]') |
||||
.pause(1000) |
||||
.modalFooterOKClick('github-credentials-error') |
||||
}, |
||||
'check the commits panel for pagination #group3': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="commits-panel"]') |
||||
.click('*[data-id="commits-panel"]') |
||||
.elements('xpath', '//*[@data-id="commits-current-branch-master"]//*[@data-type="commit-summary"]', function (result) { |
||||
console.log('Number of commit-summary elements:', (result.value as any).length); |
||||
if (useIsoGit) { |
||||
commitCount = (result.value as any).length |
||||
browser.assert.ok((result.value as any).length == 1) |
||||
} else { |
||||
commitCount = (result.value as any).length |
||||
browser.assert.ok((result.value as any).length > 2) |
||||
} |
||||
}) |
||||
|
||||
}, |
||||
'load more commits #group3': function (browser: NightwatchBrowser) { |
||||
console.log('commitCount:', commitCount) |
||||
browser |
||||
.waitForElementVisible('*[data-id="load-more-commits"]') |
||||
.click('*[data-id="load-more-commits"]') |
||||
.waitForElementVisible('*[data-id="loader-indicator"]') |
||||
.waitForElementNotPresent('*[data-id="loader-indicator"]') |
||||
.pause(2000) |
||||
.elements('xpath', '//*[@data-id="commits-current-branch-master"]//*[@data-type="commit-summary"]', function (result) { |
||||
console.log('Number of commit-summary elements:', (result.value as any).length); |
||||
browser.assert.ok((result.value as any).length > commitCount) |
||||
}) |
||||
}, |
||||
'load more branches from remote #group3': function (browser: NightwatchBrowser) { |
||||
|
||||
browser |
||||
.click('*[data-id="branches-panel"]') |
||||
.waitForElementVisible({ |
||||
selector: '//*[@data-id="branches-panel-content-remote-branches"]', |
||||
locateStrategy: 'xpath' |
||||
}) |
||||
.elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) { |
||||
console.log('Number of branches elements:', (result.value as any).length); |
||||
if (useIsoGit) { |
||||
branchCount = (result.value as any).length |
||||
browser.assert.ok((result.value as any).length == 1) |
||||
} else { |
||||
branchCount = (result.value as any).length |
||||
browser.assert.ok((result.value as any).length > 2) |
||||
} |
||||
}) |
||||
|
||||
|
||||
if (useIsoGit) { |
||||
|
||||
browser.waitForElementVisible('*[data-id="remote-sync-origin"]') |
||||
.click('*[data-id="remote-sync-origin"]') |
||||
.waitForElementVisible('*[data-id="loader-indicator"]') |
||||
.waitForElementNotPresent('*[data-id="loader-indicator"]') |
||||
.pause(2000) |
||||
.elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) { |
||||
console.log('Number of branches elements:', (result.value as any).length); |
||||
browser.assert.ok((result.value as any).length > branchCount) |
||||
}) |
||||
} else { |
||||
browser.waitForElementVisible('*[data-id="show-more-branches-on-remote"]') |
||||
.click('*[data-id="show-more-branches-on-remote"]') |
||||
.pause(1000) |
||||
.elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) { |
||||
console.log('Number of branches elements:', (result.value as any).length); |
||||
browser.assert.ok((result.value as any).length > branchCount) |
||||
}) |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
module.exports = tests |
@ -0,0 +1,90 @@ |
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
import { ChildProcess, spawn, execSync } from 'child_process' |
||||
import { homedir } from 'os' |
||||
import path from 'path' |
||||
import os from 'os' |
||||
|
||||
const dir = path.join('remix-desktop-test-' + Date.now().toString()) |
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
setuphardhat: function (browser: NightwatchBrowser) { |
||||
browser.perform(async (done) => { |
||||
await setupHardhatProject() |
||||
done() |
||||
}) |
||||
}, |
||||
addScript: function (browser: NightwatchBrowser) { |
||||
// run script in console
|
||||
browser.executeAsync(function (dir, done) { |
||||
(window as any).electronAPI.openFolderInSameWindow('/tmp/' + dir).then(done) |
||||
}, [dir], () => { |
||||
console.log('done window opened') |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewDivDraggableItemhardhat.config.js"]', 10000) |
||||
}, |
||||
compile: function (browser: NightwatchBrowser) { |
||||
browser.perform(async (done) => { |
||||
console.log('generating compilation result') |
||||
await compileHardhatProject() |
||||
done() |
||||
}) |
||||
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Hardhat').before(60000) |
||||
let addressRef |
||||
browser.clickLaunchIcon('filePanel') |
||||
.openFile('contracts') |
||||
.openFile('contracts/Token.sol') |
||||
.clickLaunchIcon('udapp') |
||||
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') |
||||
.selectContract('Token') |
||||
.createContract('') |
||||
.clickInstance(0) |
||||
.clickFunction('balanceOf - call', { types: 'address account', values: '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c' }) |
||||
.getAddressAtPosition(0, (address) => { |
||||
addressRef = address |
||||
}) |
||||
.perform((done) => { |
||||
browser.verifyCallReturnValue(addressRef, ['0:uint256: 1000000']) |
||||
.perform(() => done()) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
async function compileHardhatProject(): Promise<void> { |
||||
console.log(process.cwd()) |
||||
try { |
||||
const server = spawn('npx hardhat compile', [], { cwd: '/tmp/' + dir, shell: true, detached: true }) |
||||
return new Promise((resolve, reject) => { |
||||
server.on('exit', function (exitCode) { |
||||
console.log("Child exited with code: " + exitCode); |
||||
console.log('end') |
||||
resolve() |
||||
}) |
||||
}) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
async function setupHardhatProject(): Promise<void> { |
||||
console.log('setup hardhat project', dir) |
||||
try { |
||||
const server = spawn(`git clone https://github.com/NomicFoundation/hardhat-boilerplate ${dir} && cd ${dir} && yarn install && yarn add "@typechain/ethers-v5@^10.1.0" && yarn add "@typechain/hardhat@^6.1.2" && yarn add "typechain@^8.1.0" && echo "END"`, [], { cwd: '/tmp/', shell: true, detached: true }) |
||||
return new Promise((resolve, reject) => { |
||||
server.on('exit', function (exitCode) { |
||||
console.log("Child exited with code: " + exitCode); |
||||
console.log('end') |
||||
resolve() |
||||
}) |
||||
}) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
|
||||
module.exports = { |
||||
...tests |
||||
} |
@ -0,0 +1,48 @@ |
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
|
||||
|
||||
module.exports = { |
||||
'@offline': true, |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
'open default template': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]') |
||||
.click('button[data-id="landingPageImportFromTemplate"]') |
||||
.waitForElementPresent('*[data-id="create-remixDefault"]') |
||||
.scrollAndClick('*[data-id="create-remixDefault"]') |
||||
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') |
||||
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.pause(3000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') |
||||
.click('*[data-id="treeViewLitreeViewItemtests"]') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||
.click('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||
.waitForElementVisible('[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]') |
||||
.openFile('contracts/1_Storage.sol') |
||||
.waitForElementVisible('*[id="editorView"]', 10000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('function retrieve() public view returns (uint256){')) |
||||
}) |
||||
}) |
||||
}, |
||||
'compile storage': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clickLaunchIcon('solidity') |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="compilerContainerCompileBtn"]') |
||||
.click('[data-id="compilerContainerCompileBtn"]') |
||||
.clickLaunchIcon('filePanel') |
||||
.clickLaunchIcon('solidity') |
||||
.pause(5000) |
||||
.waitForElementPresent('*[data-id="compiledContracts"] option', 60000) |
||||
.click('*[data-id="compilation-details"]') |
||||
.waitForElementVisible('*[data-id="remixui_treeviewitem_metadata"]') |
||||
} |
||||
} |
@ -0,0 +1,269 @@ |
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
|
||||
|
||||
module.exports = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
'open default template': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]') |
||||
.click('button[data-id="landingPageImportFromTemplate"]') |
||||
.waitForElementPresent('*[data-id="create-remixDefault"]') |
||||
.scrollAndClick('*[data-id="create-remixDefault"]') |
||||
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') |
||||
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.pause(3000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') |
||||
.click('*[data-id="treeViewLitreeViewItemtests"]') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||
.click('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||
.waitForElementVisible('[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]') |
||||
.openFile('contracts/1_Storage.sol') |
||||
.waitForElementVisible('*[id="editorView"]', 10000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('function retrieve() public view returns (uint256){')) |
||||
}) |
||||
}) |
||||
}, |
||||
'Should find text #group1': function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]') |
||||
.click('*[plugin="search"]').waitForElementVisible('*[id="search_input"]') |
||||
.waitForElementVisible('*[id="search_include"]') |
||||
.setValue('*[id="search_include"]', ', *.*').pause(2000) |
||||
.setValue('*[id="search_input"]', 'read').sendKeys('*[id="search_input"]', browser.Keys.ENTER) |
||||
.pause(1000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', '3_BALLOT.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'contracts', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'README.TXT', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'file must') |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'be compiled') |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'that person al') |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'sender.voted') |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'read') |
||||
.elements('css selector', '.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 6) |
||||
}) |
||||
}, 'Should find text with exclude #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'contract').pause(1000) |
||||
.clearValue('*[id="search_include"]').pause(2000) |
||||
.setValue('*[id="search_include"]', '**').sendKeys('*[id="search_include"]', browser.Keys.ENTER).pause(4000) |
||||
.elements('css selector', '.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 62) |
||||
}) |
||||
.setValue('*[id="search_exclude"]', ',contracts/**').sendKeys('*[id="search_exclude"]', browser.Keys.ENTER).pause(4000) |
||||
.elements('css selector', '.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 56) |
||||
}) |
||||
.clearValue('*[id="search_include"]').setValue('*[id="search_include"]', '*.sol, *.js, *.txt') |
||||
.clearValue('*[id="search_exclude"]').setValue('*[id="search_exclude"]', '.*/**/*') |
||||
}, |
||||
'Should find regex #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[id="search_input"]') |
||||
.clearValue('*[id="search_input"]').pause(2000) |
||||
.setValue('*[id="search_input"]', '^contract').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(3000) |
||||
.waitForElementVisible('*[data-id="search_use_regex"]').click('*[data-id="search_use_regex"]').pause(3000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', '3_BALLOT.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', '2_OWNER.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', '1_STORAGE.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'BALLOT_TEST.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'tests', 60000) |
||||
.elements('css selector', '.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 4) |
||||
}) |
||||
}, |
||||
'Should find matchcase #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="search_use_regex"]').click('*[data-id="search_use_regex"]') |
||||
.waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]').pause(4000) |
||||
.elements('css selector', '.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 0) |
||||
}) |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'Contract').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(3000) |
||||
.elements('css selector', '.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 3) |
||||
}) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'STORAGE.TEST.JS', 60000) |
||||
}, |
||||
'Should find matchword #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]') |
||||
.waitForElementVisible('*[data-id="search_whole_word"]').click('*[data-id="search_whole_word"]').pause(2000) |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'contract').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(4000) |
||||
.elements('css selector', '.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 15) |
||||
}) |
||||
}, |
||||
'Should replace text #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="toggle_replace"]').click('*[data-id="toggle_replace"]') |
||||
.waitForElementVisible('*[id="search_replace"]') |
||||
.clearValue('*[id="search_include"]').setValue('*[id="search_include"]', 'contracts/2_*.sol') |
||||
.setValue('*[id="search_replace"]', 'replacing').sendKeys('*[id="search_include"]', browser.Keys.ENTER).pause(1000) |
||||
.waitForElementVisible('*[data-id="contracts/2_Owner.sol-33-71"]') |
||||
.moveToElement('*[data-id="contracts/2_Owner.sol-33-71"]', 10, 10) |
||||
.waitForElementVisible('*[data-id="replace-contracts/2_Owner.sol-33-71"]') |
||||
.click('*[data-id="replace-contracts/2_Owner.sol-33-71"]').pause(2000). |
||||
modalFooterOKClick('confirmreplace').pause(2000). |
||||
getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('replacing deployer for a constructor'), 'should replace text ok') |
||||
}) |
||||
}, |
||||
'Should replace text without confirmation #group1': function (browser: NightwatchBrowser) { |
||||
browser.click('*[data-id="confirm_replace_label"]').pause(500) |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'replacing').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000) |
||||
.setValue('*[id="search_replace"]', 'replacing2').pause(1000) |
||||
.waitForElementVisible('*[data-id="contracts/2_Owner.sol-33-71"]') |
||||
.moveToElement('*[data-id="contracts/2_Owner.sol-33-71"]', 10, 10) |
||||
.waitForElementVisible('*[data-id="replace-contracts/2_Owner.sol-33-71"]') |
||||
.click('*[data-id="replace-contracts/2_Owner.sol-33-71"]').pause(2000). |
||||
getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('replacing2 deployer for a constructor'), 'should replace text ok') |
||||
}) |
||||
}, |
||||
'Should replace all & undo #group1': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clearValue('*[id="search_input"]') |
||||
.clearValue('*[id="search_include"]').setValue('*[id="search_include"]', 'contracts/1_*.sol') |
||||
.setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_include"]', browser.Keys.ENTER) |
||||
.clearValue('*[id="search_replace"]') |
||||
.setValue('*[id="search_replace"]', '123test').pause(1000) |
||||
.waitForElementVisible('*[data-id="replace-all-contracts/1_Storage.sol"]') |
||||
.click('*[data-id="replace-all-contracts/1_Storage.sol"]').pause(2000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('contract 123test'), 'should replace text ok') |
||||
browser.assert.ok(content.includes('title 123test'), 'should replace text ok') |
||||
}) |
||||
.waitForElementVisible('*[data-id="undo-replace-contracts/1_Storage.sol"]') |
||||
.click('*[data-id="undo-replace-contracts/1_Storage.sol"]').pause(2000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('contract Storage'), 'should undo text ok') |
||||
browser.assert.ok(content.includes('title Storage'), 'should undo text ok') |
||||
}) |
||||
}, |
||||
'Should replace all & undo & switch between files #group1': function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[id="search_input"]') |
||||
.clearValue('*[id="search_input"]') |
||||
.clearValue('*[id="search_include"]').setValue('*[id="search_include"]', '*.sol, *.js, *.txt') |
||||
.setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_include"]', browser.Keys.ENTER) |
||||
.clearValue('*[id="search_replace"]') |
||||
.setValue('*[id="search_replace"]', '123test').pause(1000) |
||||
.waitForElementVisible('*[data-id="replace-all-contracts/1_Storage.sol"]') |
||||
.click('*[data-id="replace-all-contracts/1_Storage.sol"]').pause(2000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('contract 123test'), 'should replace text ok') |
||||
browser.assert.ok(content.includes('title 123test'), 'should replace text ok') |
||||
}) |
||||
.waitForElementVisible('*[data-id="undo-replace-contracts/1_Storage.sol"]') |
||||
.openFile('README.txt') |
||||
.click('*[plugin="search"]').pause(2000) |
||||
.waitForElementNotPresent('*[data-id="undo-replace-contracts/1_Storage.sol"]') |
||||
.waitForElementVisible('*[data-id="replace-all-README.txt"]') |
||||
.click('*[data-id="replace-all-README.txt"]').pause(2000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes("123test' contract"), 'should replace text ok') |
||||
}) |
||||
.waitForElementVisible('*[data-id="undo-replace-README.txt"]') |
||||
.click('div[data-path="/contracts/1_Storage.sol"]').pause(2000) |
||||
.waitForElementVisible('*[data-id="undo-replace-contracts/1_Storage.sol"]') |
||||
.click('*[data-id="undo-replace-contracts/1_Storage.sol"]').pause(2000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('contract Storage'), 'should undo text ok') |
||||
browser.assert.ok(content.includes('title Storage'), 'should undo text ok') |
||||
}) |
||||
.click('div[data-path="/README.txt"]').pause(2000) |
||||
.waitForElementVisible('*[data-id="undo-replace-README.txt"]') |
||||
.click('*[data-id="undo-replace-README.txt"]').pause(2000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes("Storage' contract"), 'should replace text ok') |
||||
}) |
||||
}, |
||||
'Should hide button when edited content is the same #group2': function (browser: NightwatchBrowser) { |
||||
browser.refresh() |
||||
.waitForElementVisible('*[data-id="remixIdeSidePanel"]') |
||||
.addFile('test.sol', { content: '123' }) |
||||
.pause(4000) |
||||
.click('*[plugin="search"]') |
||||
.waitForElementVisible('*[id="search_input"]') |
||||
.waitForElementVisible('*[data-id="toggle_replace"]') |
||||
.click('*[data-id="toggle_replace"]') |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', '123') |
||||
.sendKeys('*[id="search_input"]', browser.Keys.ENTER) |
||||
.waitForElementVisible('*[id="search_replace"]') |
||||
.clearValue('*[id="search_replace"]') |
||||
.setValue('*[id="search_replace"]', '456').pause(1000) |
||||
.click('*[data-id="confirm_replace_label"]').pause(500) |
||||
.waitForElementVisible('*[data-id="replace-all-test.sol"]') |
||||
.click('*[data-id="replace-all-test.sol"]').pause(2000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('456'), 'should replace text ok') |
||||
} |
||||
) |
||||
.setEditorValue('123') |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('123'), 'should have text ok') |
||||
} |
||||
).pause(5000) |
||||
.waitForElementNotPresent('*[data-id="undo-replace-test.sol"]') |
||||
}, |
||||
'Should disable/enable button when edited content changed #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[id="search_input"]') |
||||
.clearValue('*[id="search_input"]') |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', '123').sendKeys('*[id="search_input"]', browser.Keys.ENTER) |
||||
.clearValue('*[id="search_replace"]') |
||||
.setValue('*[id="search_replace"]', 'replaced').pause(1000) |
||||
.waitForElementVisible('*[data-id="replace-all-test.sol"]') |
||||
.click('*[data-id="replace-all-test.sol"]').pause(2000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('replaced'), 'should replace text ok') |
||||
} |
||||
) |
||||
.setEditorValue('changed') |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('changed'), 'should have text ok') |
||||
} |
||||
).pause(5000) |
||||
.waitForElementVisible('*[data-id="undo-replace-test.sol"]') |
||||
.getAttribute('[data-id="undo-replace-test.sol"]', 'disabled', (result) => { |
||||
browser.assert.equal(result.value, 'true', 'should be disabled') |
||||
}) |
||||
.setEditorValue('replaced') |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('replaced'), 'should have text ok') |
||||
} |
||||
).pause(1000) |
||||
.waitForElementVisible('*[data-id="undo-replace-test.sol"]') |
||||
.getAttribute('[data-id="undo-replace-test.sol"]', 'disabled', (result) => { |
||||
browser.assert.equal(result.value, null, 'should not be disabled') |
||||
}) |
||||
.click('*[data-id="undo-replace-test.sol"]').pause(2000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('123'), 'should have text ok') |
||||
}) |
||||
.waitForElementNotPresent('*[data-id="undo-replace-test.sol"]') |
||||
}, |
||||
|
||||
'should clear search #group2': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'nodata').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000) |
||||
.elements('css selector', '.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 0) |
||||
}) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,107 @@ |
||||
import {NightwatchBrowser} from 'nightwatch' |
||||
import { ChildProcess, spawn, execSync } from 'child_process' |
||||
import { homedir } from 'os' |
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
open: function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[data-id="openFolderButton"]', 10000).click('*[data-id="openFolderButton"]') |
||||
}, |
||||
|
||||
'open default template': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]') |
||||
.click('button[data-id="landingPageImportFromTemplate"]') |
||||
.waitForElementPresent('*[data-id="create-remixDefault"]') |
||||
.scrollAndClick('*[data-id="create-remixDefault"]') |
||||
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') |
||||
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.pause(3000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') |
||||
.click('*[data-id="treeViewLitreeViewItemtests"]') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||
.click('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||
.waitForElementVisible('[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]') |
||||
.openFile('contracts/1_Storage.sol') |
||||
.waitForElementVisible('*[id="editorView"]', 10000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('function retrieve() public view returns (uint256){')) |
||||
}) |
||||
}) |
||||
}, |
||||
'Should install slither #group6': function (browser: NightwatchBrowser) { |
||||
browser.perform(async (done) => { |
||||
await installSlither() |
||||
done() |
||||
}) |
||||
}, |
||||
'run slither': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.click('[data-id="verticalIconsKindpluginManager"]') |
||||
.scrollAndClick('[data-id="pluginManagerComponentActivateButtonsolidityStaticAnalysis"]') |
||||
.clickLaunchIcon('solidity').click('*[data-id="compilerContainerCompileBtn"]') |
||||
.pause(1000) |
||||
.clickLaunchIcon('solidityStaticAnalysis') |
||||
.useXpath() |
||||
.click('//*[@id="staticAnalysisRunBtn"]') |
||||
.waitForElementPresent('//*[@id="staticanalysisresult"]', 5000) |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-id='nolibslitherwarnings'][contains(text(), '1')]", |
||||
locateStrategy: 'xpath', |
||||
timeout: 5000 |
||||
}) |
||||
.waitForElementVisible({ |
||||
selector: "//div[@data-id='block']/span[contains(text(), '1 warnings found.')]", |
||||
locateStrategy: 'xpath', |
||||
timeout: 5000 |
||||
}) |
||||
}, |
||||
|
||||
after: function (browser: NightwatchBrowser) { |
||||
browser.end() |
||||
}, |
||||
} |
||||
|
||||
async function installSlither(): Promise<void> { |
||||
console.log('installSlither', process.cwd()) |
||||
try { |
||||
try { |
||||
const solcVersion = '0.8.15' |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: requires Python3.6+ (pip3) to be installed on your system`) |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: solc-select will be installed along with Slither to set different solc compiler versions.`) |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: checking pip3 availability ...`) |
||||
const pip3OP = execSync('pip3 --version') |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: pip3 found: ${pip3OP.toString()}`) |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: installing slither...`) |
||||
const slitherOP = execSync('pip3 install slither-analyzer') |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: slither installation output: ${slitherOP.toString()}`) |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: installing solc-select...`) |
||||
const solcSelectOP = execSync('pip3 install solc-select') |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: solc-select installation output: ${solcSelectOP.toString()}`) |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: installing solc ${solcVersion}...`) |
||||
const solcInstallOP = execSync(`solc-select install ${solcVersion}`) |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: solc installation output: ${solcInstallOP.toString()}`) |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: setting solc version to ${solcVersion}...`) |
||||
const solcUseOP = execSync(`solc-select use ${solcVersion}`) |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: solc setting installation output: ${solcUseOP.toString()}`) |
||||
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: Slither is ready to use!`) |
||||
} catch (err) { |
||||
console.log('\x1b[31m%s\x1b[0m', `[Slither Installation]: Error occurred: ${err}`) |
||||
} |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
module.exports = { |
||||
...process.platform.startsWith('linux')?tests:{} |
||||
} |
@ -0,0 +1,35 @@ |
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
|
||||
|
||||
module.exports = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
'open default template': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]') |
||||
.click('button[data-id="landingPageImportFromTemplate"]') |
||||
.waitForElementPresent('*[data-id="create-remixDefault"]') |
||||
.scrollAndClick('*[data-id="create-remixDefault"]') |
||||
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') |
||||
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok') |
||||
.pause(3000) |
||||
.windowHandles(function (result) { |
||||
console.log(result.value) |
||||
browser.switchWindow(result.value[1]) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') |
||||
.click('*[data-id="treeViewLitreeViewItemtests"]') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||
.click('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||
.waitForElementVisible('[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]') |
||||
.openFile('contracts/1_Storage.sol') |
||||
.waitForElementVisible('*[id="editorView"]', 10000) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('function retrieve() public view returns (uint256){')) |
||||
}) |
||||
}) |
||||
.end() |
||||
} |
||||
} |
@ -0,0 +1,246 @@ |
||||
import {NightwatchBrowser} from 'nightwatch' |
||||
|
||||
|
||||
|
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
open: function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[data-id="openFolderButton"]', 10000).click('*[data-id="openFolderButton"]') |
||||
}, |
||||
'open xterm linux and create a file': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="tabXTerm"]', 10000) |
||||
.click('*[data-id="tabXTerm"]') |
||||
.waitForElementVisible('*[data-type="remixUIXT"]', 10000) |
||||
.click('*[data-type="remixUIXT"]') |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('echo test > example.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemexample.txt"]', 10000) |
||||
}, |
||||
'rename that file': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('mv example.txt newExample.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemnewExample.txt"]', 10000) |
||||
}, |
||||
'create a file and delete it': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('touch newExample2.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemnewExample2.txt"]', 10000) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('rm newExample2.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementNotPresent('*[data-id="treeViewLitreeViewItemnewExample2.txt"]', 10000) |
||||
}, |
||||
'run a git clone command': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('git clone https://github.com/ethereum/awesome-remix').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemawesome-remix"]', 10000) |
||||
.click('*[data-id="treeViewLitreeViewItemawesome-remix"]') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemawesome-remix/README.md"]', 10000) |
||||
}, |
||||
'remove the cloned repo': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-type="remixUIXT"]', 10000) |
||||
.click('*[data-type="remixUIXT"]') |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('rm -rf awesome-remix').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementNotPresent('*[data-id="treeViewLitreeViewItemawesome-remix"]', 10000) |
||||
}, |
||||
'list files': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(2000) |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.click({ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('ls').sendKeys(this.Keys.ENTER) |
||||
}).pause(3000) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok((result.value as string).includes('newExample.txt')) |
||||
} |
||||
) |
||||
}, |
||||
'switch to a new terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="createTerminalButton"]', 10000) |
||||
.click('*[data-id="createTerminalButton"]') |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.assert.ok((result.value as any).length === 2) |
||||
}) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok(!(result.value as string).includes('newExample.txt')) |
||||
} |
||||
) |
||||
}, |
||||
'switch to a third terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="createTerminalButton"]', 10000) |
||||
.click('*[data-id="createTerminalButton"]') |
||||
.waitForElementVisible( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
10000 |
||||
) |
||||
.click({ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.assert.ok((result.value as any).length === 3) |
||||
}) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('echo thirdterminal').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
}, |
||||
'switch back to the second terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.elementIdClick(Object.values((result.value as any)[1])[0] as any) |
||||
}) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok(!(result.value as string).includes('newExample.txt')) |
||||
} |
||||
) |
||||
}, |
||||
'close the second terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="closeTerminalButton"]', 10000) |
||||
.click('*[data-id="closeTerminalButton"]') |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.assert.ok((result.value as any).length === 2) |
||||
}) |
||||
}, |
||||
'switch back to the first terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.elementIdClick(Object.values((result.value as any)[0])[0] as any) |
||||
}) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok((result.value as string).includes('newExample.txt')) |
||||
} |
||||
) |
||||
}, |
||||
'switch to the output panel': function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[data-id="tabOutput"]', 10000).click('*[data-id="tabOutput"]').waitForElementNotPresent('*[data-id="createTerminalButton"]', 10000) |
||||
}, |
||||
'switch back to xterminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="tabXTerm"]', 10000) |
||||
.click('*[data-id="tabXTerm"]') |
||||
.waitForElementVisible('*[data-type="remixUIXT"]', 10000) |
||||
.click('*[data-type="remixUIXT"]') |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok((result.value as string).includes('newExample.txt')) |
||||
} |
||||
) |
||||
}, |
||||
'clear the terminal and type exit': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(1000) |
||||
.waitForElementVisible('*[data-id="clearTerminalButton"]', 10000) |
||||
.click('*[data-id="clearTerminalButton"]') |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok(!(result.value as string).includes('newExample.txt')) |
||||
.waitForElementVisible( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
10000 |
||||
) |
||||
.click({ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.pause(1000) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('exit').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.pause(1000) |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.assert.ok((result.value as any).length === 1) |
||||
}).end() |
||||
} |
||||
).pause(3000) |
||||
}, |
||||
} |
||||
|
||||
|
||||
module.exports = { |
||||
...process.platform.startsWith('win')?{}:tests |
||||
} |
@ -0,0 +1,249 @@ |
||||
import {NightwatchBrowser} from 'nightwatch' |
||||
|
||||
const tests = { |
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
done() |
||||
}, |
||||
open: function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[data-id="openFolderButton"]', 10000).click('*[data-id="openFolderButton"]') |
||||
}, |
||||
'open xterm window and create a file': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="tabXTerm"]', 10000) |
||||
.click('*[data-id="tabXTerm"]') |
||||
.waitForElementVisible('*[data-id="select_shell"]') |
||||
.click('*[data-id="select_shell"]') |
||||
.waitForElementVisible('*[data-id="select_powershell.exe"]') |
||||
.click('*[data-id="select_powershell.exe"]') |
||||
.pause(3000) |
||||
.waitForElementVisible("[data-active='1'][data-type='remixUIXT']", 10000) |
||||
.click("[data-active='1'][data-type='remixUIXT']") |
||||
.pause(1000) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('"test" | Out-File -FilePath example.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemexample.txt"]', 10000) |
||||
}, |
||||
'rename that file': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('Move-Item -Path example.txt -Destination newExample.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemnewExample.txt"]', 10000) |
||||
}, |
||||
'create a file and delete it': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('touch newExample2.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemnewExample2.txt"]', 10000) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('Remove-Item -Path newExample2.txt').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementNotPresent('*[data-id="treeViewLitreeViewItemnewExample2.txt"]', 10000) |
||||
}, |
||||
'run a git clone command': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('git clone https://github.com/ethereum/awesome-remix').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemawesome-remix"]', 10000) |
||||
.click('*[data-id="treeViewLitreeViewItemawesome-remix"]') |
||||
.waitForElementVisible('*[data-id="treeViewLitreeViewItemawesome-remix/README.md"]', 10000) |
||||
}, |
||||
'remove the cloned repo': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible("[data-active='1'][data-type='remixUIXT']", 10000) |
||||
.click("[data-active='1'][data-type='remixUIXT']") |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('Remove-Item -Path awesome-remix -Recurse -Force').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.waitForElementNotPresent('*[data-id="treeViewLitreeViewItemawesome-remix"]', 10000) |
||||
}, |
||||
'list files': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.pause(3000) |
||||
.waitForElementVisible("[data-active='1'][data-type='remixUIXT']", 10000) |
||||
.click("[data-active='1'][data-type='remixUIXT']") |
||||
.saveScreenshot('./reports/screenshots/list-files.png') |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('ls').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
.saveScreenshot('./reports/screenshots/list-files-after.png') |
||||
.waitForElementVisible({ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.pause(2000) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok((result.value as string).includes('newExample')) |
||||
} |
||||
) |
||||
}, |
||||
'switch to a new terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="select_shell"]') |
||||
.click('*[data-id="select_shell"]') |
||||
.waitForElementVisible('*[data-id="select_powershell.exe"]') |
||||
.click('*[data-id="select_powershell.exe"]') |
||||
.pause(3000) |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
console.log(result) |
||||
browser.assert.ok((result.value as any).length === 3) |
||||
}) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok(!(result.value as string).includes('newExample')) |
||||
} |
||||
) |
||||
}, |
||||
'switch to a third terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="select_shell"]') |
||||
.click('*[data-id="select_shell"]') |
||||
.waitForElementVisible('*[data-id="select_powershell.exe"]') |
||||
.click('*[data-id="select_powershell.exe"]') |
||||
.pause(3000) |
||||
.waitForElementVisible( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
10000 |
||||
) |
||||
.click({ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}) |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.assert.ok((result.value as any).length === 4) |
||||
}) |
||||
.perform(function () { |
||||
const actions = this.actions({async: true}) |
||||
return actions.sendKeys('echo thirdterminal').sendKeys(this.Keys.ENTER) |
||||
}) |
||||
}, |
||||
'switch back to the second terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.elementIdClick(Object.values((result.value as any)[2])[0] as any) |
||||
}) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok(!(result.value as string).includes('newExample')) |
||||
} |
||||
) |
||||
}, |
||||
'close the second terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="closeTerminalButton"]', 10000) |
||||
.click('*[data-id="closeTerminalButton"]') |
||||
.pause(1000) |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.assert.ok((result.value as any).length === 3) |
||||
}) |
||||
}, |
||||
'switch back to the first terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.elementIdClick(Object.values((result.value as any)[1])[0] as any) |
||||
}) |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok((result.value as string).includes('newExample')) |
||||
} |
||||
) |
||||
}, |
||||
'switch to the output panel': function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[data-id="tabOutput"]', 10000).click('*[data-id="tabOutput"]').waitForElementNotPresent('*[data-id="createTerminalButton"]', 10000) |
||||
}, |
||||
'switch back to xterminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="tabXTerm"]', 10000) |
||||
.click('*[data-id="tabXTerm"]') |
||||
.waitForElementVisible("[data-active='1'][data-type='remixUIXT']", 10000) |
||||
.click("[data-active='1'][data-type='remixUIXT']") |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok((result.value as string).includes('newExample')) |
||||
} |
||||
) |
||||
}, |
||||
'clear the terminal': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="clearTerminalButton"]', 10000) |
||||
.click('*[data-id="clearTerminalButton"]') |
||||
.getText( |
||||
{ |
||||
selector: "//*[@data-type='remixUIXT' and @data-active='1']", |
||||
timeout: 10000, |
||||
locateStrategy: 'xpath', |
||||
}, |
||||
function (result) { |
||||
console.log('Text content of the element:', result.value) |
||||
browser.assert.ok(!(result.value as string).includes('newExample')) |
||||
} |
||||
) |
||||
}, |
||||
'close all terminals': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="closeTerminalButton"]', 10000) |
||||
.click('*[data-id="closeTerminalButton"]') |
||||
.pause(3000) |
||||
.click('*[data-id="closeTerminalButton"]') |
||||
.pause(3000) |
||||
.click('*[data-id="closeTerminalButton"]') |
||||
.pause(3000) |
||||
.elements('css selector', '[data-type="remixUIXTSideButton"]', function (result) { |
||||
browser.assert.ok((result.value as any).length === 0) |
||||
}).end() |
||||
}, |
||||
after: function (browser: NightwatchBrowser) { |
||||
browser.end() |
||||
}, |
||||
} |
||||
|
||||
module.exports = { |
||||
...process.platform.startsWith('win')?tests:{} |
||||
} |
@ -0,0 +1,108 @@ |
||||
// Merge custom command types with nightwatch types
|
||||
/* eslint-disable no-use-before-define */ |
||||
import {NightwatchBrowser} from 'nightwatch' // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
export type callbackCheckVerifyCallReturnValue = (values: string[]) => {message: string; pass: boolean} |
||||
|
||||
declare module 'nightwatch' { |
||||
export interface NightwatchCustomCommands { |
||||
clickLaunchIcon(icon: string): NightwatchBrowser |
||||
switchBrowserTab(index: number): NightwatchBrowser |
||||
scrollAndClick(target: string): NightwatchBrowser |
||||
scrollInto(target: string): NightwatchBrowser |
||||
testContracts(fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser |
||||
setEditorValue(value: string, callback?: () => void): NightwatchBrowser |
||||
addFile(name: string, content: NightwatchContractContent, readMeFile?: string): NightwatchBrowser |
||||
verifyContracts(compiledContractNames: string[], opts?: {wait: number; version?: string; runs?: string}): NightwatchBrowser |
||||
selectAccount(account?: string): NightwatchBrowser |
||||
clickFunction(fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser |
||||
testFunction(txHash: string, expectedInput: NightwatchTestFunctionExpectedInput): NightwatchBrowser |
||||
goToVMTraceStep(step: number, incr?: number): NightwatchBrowser |
||||
checkVariableDebug(id: string, debugValue: NightwatchCheckVariableDebugValue): NightwatchBrowser |
||||
addAtAddressInstance(address: string, isValidFormat: boolean, isValidChecksum: boolean, isAbi?: boolean): NightwatchBrowser |
||||
modalFooterOKClick(id?: string): NightwatchBrowser |
||||
clickInstance(index: number): NightwatchBrowser |
||||
journalLastChildIncludes(val: string): NightwatchBrowser |
||||
executeScriptInTerminal(script: string): NightwatchBrowser |
||||
clearEditableContent(cssSelector: string): NightwatchBrowser |
||||
journalChildIncludes(val: string, opts = {shouldHaveOnlyOneOccurence: boolean}): NightwatchBrowser |
||||
debugTransaction(index: number): NightwatchBrowser |
||||
checkElementStyle(cssSelector: string, styleProperty: string, expectedResult: string): NightwatchBrowser |
||||
openFile(name: string): NightwatchBrowser |
||||
refreshPage(): NightwatchBrowser |
||||
verifyLoad(): NightwatchBrowser |
||||
renamePath(path: string, newFileName: string, renamedPath: string): NightwatchBrowser |
||||
rightClickCustom(cssSelector: string): NightwatchBrowser |
||||
scrollToLine(line: number): NightwatchBrowser |
||||
waitForElementContainsText(id: string, value: string, timeout?: number): NightwatchBrowser |
||||
getModalBody(callback: (value: string, cb: VoidFunction) => void): NightwatchBrowser |
||||
modalFooterCancelClick(id?: string): NightwatchBrowser |
||||
selectContract(contractName: string): NightwatchBrowser |
||||
createContract(inputParams: string): NightwatchBrowser |
||||
getAddressAtPosition(index: number, cb: (pos: string) => void): NightwatchBrowser |
||||
testConstantFunction(address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput | null, expectedOutput: string): NightwatchBrowser |
||||
getEditorValue(callback: (content: string) => void): NightwatchBrowser |
||||
getInstalledPlugins(cb: (plugins: string[]) => void): NightwatchBrowser |
||||
verifyCallReturnValue(address: string, checks: string[] | callbackCheckVerifyCallReturnValue): NightwatchBrowser |
||||
testEditorValue(testvalue: string): NightwatchBrowser |
||||
removeFile(path: string, workspace: string): NightwatchBrowser |
||||
switchBrowserWindow(url: string, windowName: string, cb: (browser: NightwatchBrowser, window?: NightwatchCallbackResult<Window>) => void): NightwatchBrowser |
||||
setupMetamask(passphrase: string, password: string): NightwatchBrowser |
||||
signMessage(msg: string, callback: (hash: {value: string}, signature: {value: string}) => void): NightwatchBrowser |
||||
setSolidityCompilerVersion(version: string): NightwatchBrowser |
||||
clickElementAtPosition(cssSelector: string, index: number, opt?: {forceSelectIfUnselected: boolean}): NightwatchBrowser |
||||
notContainsText(cssSelector: string, text: string): NightwatchBrowser |
||||
sendLowLevelTx(address: string, value: string, callData: string): NightwatchBrowser |
||||
journalLastChild(val: string): NightwatchBrowser |
||||
checkTerminalFilter(filter: string, test: string): NightwatchBrowser |
||||
noWorkerErrorFor(version: string): NightwatchBrowser |
||||
validateValueInput(selector: string, valueTosSet: string[], expectedValue: string): NightwatchBrowser |
||||
checkAnnotations(type: string): NightwatchBrowser |
||||
checkAnnotationsNotPresent(type: string): NightwatchBrowser |
||||
getLastTransactionHash(callback: (hash: string) => void) |
||||
currentWorkspaceIs(name: string): NightwatchBrowser |
||||
addLocalPlugin(this: NightwatchBrowser, profile: Profile & LocationProfile & ExternalProfile): NightwatchBrowser |
||||
acceptAndRemember(this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser |
||||
clearConsole(this: NightwatchBrowser): NightwatchBrowser |
||||
clearTransactions(this: NightwatchBrowser): NightwatchBrowser |
||||
getBrowserLogs(this: NightwatchBrowser): NightwatchBrowser |
||||
currentSelectedFileIs(name: string): NightwatchBrowser |
||||
switchWorkspace: (workspaceName: string) => NightwatchBrowser |
||||
switchEnvironment: (provider: string) => NightwatchBrowser |
||||
connectToExternalHttpProvider: (url: string, identifier: string) => NightwatchBrowser |
||||
waitForElementNotContainsText: (id: string, value: string, timeout: number = 10000) => NightwatchBrowser |
||||
hideToolTips: (this: NightwatchBrowser) => NightwatchBrowser |
||||
enableClipBoard: () => NightwatchBrowser |
||||
} |
||||
|
||||
export interface NightwatchBrowser { |
||||
api: this |
||||
emit: (status: string) => void |
||||
fullscreenWindow: (result?: any) => this |
||||
keys(keysToSend: string, callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void): NightwatchBrowser |
||||
sendKeys: (selector: string, inputValue: string | string[], callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void) => NightwatchBrowser |
||||
} |
||||
|
||||
export interface NightwatchAPI { |
||||
keys(keysToSend: string, callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void): NightwatchAPI |
||||
} |
||||
|
||||
export interface NightwatchContractContent { |
||||
content: string |
||||
} |
||||
|
||||
export interface NightwatchClickFunctionExpectedInput { |
||||
types: string |
||||
values: string |
||||
} |
||||
|
||||
export interface NightwatchTestFunctionExpectedInput { |
||||
[key: string]: any |
||||
} |
||||
|
||||
export interface NightwatchTestConstantFunctionExpectedInput { |
||||
types: string |
||||
values: string |
||||
} |
||||
|
||||
export type NightwatchCheckVariableDebugValue = NightwatchTestFunctionExpectedInput |
||||
} |
@ -0,0 +1,8 @@ |
||||
{ |
||||
"extends": "./tsconfig.json", |
||||
"compilerOptions": { |
||||
"outDir": "build-e2e", |
||||
"target": "ES6" |
||||
}, |
||||
"include": ["test/**/*.ts", "test/**/*.js", "../remix-ide-e2e/src/commands"] |
||||
} |
@ -1,17 +1,35 @@ |
||||
{ |
||||
"compilerOptions": { |
||||
"target": "es6", |
||||
"module": "commonjs", |
||||
"target": "ES2018", |
||||
"allowJs": true, |
||||
"module": "CommonJS", |
||||
"skipLibCheck": true, |
||||
"esModuleInterop": true, |
||||
"noImplicitAny": true, |
||||
"allowSyntheticDefaultImports": true, |
||||
"noImplicitAny": false, |
||||
"sourceMap": true, |
||||
"strictPropertyInitialization": false, |
||||
"strict": true, |
||||
"outDir": "build", |
||||
"rootDir": "./src/", |
||||
"noEmitOnError": true, |
||||
"typeRoots": ["node_modules/@types", "./types"] |
||||
} |
||||
"baseUrl": ".", |
||||
"outDir": "./build", |
||||
"moduleResolution": "node", |
||||
"resolveJsonModule": true, |
||||
"paths": { |
||||
"*": [ |
||||
"node_modules/*" |
||||
], |
||||
"@remix-api": [ |
||||
"../../libs/remix-api/src/lib/types/git.ts" |
||||
], |
||||
"@remix-git": [ |
||||
"../../libs/remix-git/" |
||||
], |
||||
}, |
||||
"typeRoots": [ |
||||
"src/**/*.d.ts", |
||||
"node_modules/@types", |
||||
"test/**/*.d.ts", |
||||
"../remix-ide-e2e/src/**/*.d.ts" |
||||
] |
||||
}, |
||||
"include": [ |
||||
"src/**/*", |
||||
] |
||||
} |
@ -0,0 +1,44 @@ |
||||
const path = require('path'); |
||||
const nodeExternals = require('webpack-node-externals'); |
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); |
||||
const mode = process.env.NODE_ENV || 'development'; |
||||
const webpack = require('webpack'); |
||||
module.exports = { |
||||
mode, |
||||
entry: { |
||||
main: './src/main.ts', |
||||
preload: './src/preload.ts', |
||||
}, |
||||
target: 'electron-main', |
||||
externals: [nodeExternals()], |
||||
module: { |
||||
rules: [ |
||||
{ |
||||
test: /\.ts$/, |
||||
include: /src/, |
||||
use: [{ loader: 'ts-loader' }] |
||||
}, |
||||
{ |
||||
test: /\.node$/, |
||||
use: 'node-loader' |
||||
} |
||||
] |
||||
}, |
||||
resolve: { |
||||
extensions: ['.ts', '.js'], |
||||
plugins: [new TsconfigPathsPlugin({ configFile: './tsconfig.json' })] |
||||
}, |
||||
plugins: [ |
||||
new webpack.DefinePlugin({ |
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || mode) |
||||
}) |
||||
], |
||||
output: { |
||||
path: path.resolve(__dirname, 'build'), |
||||
filename: '[name].js' |
||||
}, |
||||
node: { |
||||
__dirname: false, |
||||
__filename: false |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1 +1,2 @@ |
||||
export * from './lib/remix-api' |
||||
export * from './lib/types/git' |
@ -1,10 +1,13 @@ |
||||
import { commitChange } from "@remix-ui/git"; |
||||
import { commitChange } from "@remix-api"; |
||||
import { IFileSystem } from "@remixproject/plugin-api" |
||||
|
||||
// Extended interface with 'diff' method
|
||||
export interface IExtendedFileSystem extends IFileSystem { |
||||
methods: IFileSystem['methods'] & { |
||||
/** Compare the differences between two files */ |
||||
diff(change: commitChange): Promise<void> |
||||
refresh(): Promise<void> |
||||
hasGitSubmodules(): Promise<boolean> |
||||
isGitRepo(): Promise<boolean> |
||||
}; |
||||
} |
@ -0,0 +1,13 @@ |
||||
import { StatusEvents } from "@remixproject/plugin-utils"; |
||||
|
||||
export interface IFs { |
||||
events: { |
||||
workingDirChanged(path: string): Promise<void>, |
||||
} & StatusEvents, |
||||
methods: { |
||||
selectFolder(path?: string, title?: string, button?: string): Promise<string>
|
||||
openWindow(path?: string): Promise<void>, |
||||
getWorkingDir(): Promise<string>, |
||||
openFolderInSameWindow(path: string): Promise<void>, |
||||
} |
||||
} |
@ -0,0 +1,43 @@ |
||||
import { StatusEvents } from "@remixproject/plugin-utils" |
||||
import { ReadBlobResult, ReadCommitResult, StatusRow } from "isomorphic-git" |
||||
import { commitChange, repositoriesInput, repository, cloneInputType, branchesInputType, branch, remote, logInputType, remoteCommitsInputType, pagedCommits, fetchInputType, pullInputType, pushInputType, currentBranchInput, branchInputType, checkoutInputType, addInputType, rmInputType, resolveRefInput, readBlobInput, commitInputType, statusInput, compareBranchesInput, branchDifference, initInputType, updateSubmodulesInput } from "../types/git" |
||||
|
||||
export interface IGitApi { |
||||
events: { |
||||
"checkout": () => void |
||||
"clone": () => void |
||||
"add": () => void |
||||
"rm": () => void |
||||
"commit": () => void |
||||
"branch": () => void |
||||
"init": () => void |
||||
} & StatusEvents, |
||||
methods: { |
||||
getCommitChanges(oid1: string, oid2: string): Promise<commitChange[]> |
||||
repositories(input: repositoriesInput): Promise<repository[]> |
||||
clone(input: cloneInputType): Promise<any> |
||||
branches(input?: branchesInputType): Promise<branch[]>, |
||||
remotes(): Promise<remote[]>, |
||||
log(input: logInputType): Promise<ReadCommitResult[]>, |
||||
remotecommits(input: remoteCommitsInputType): Promise<pagedCommits[]> |
||||
fetch(input: fetchInputType): Promise<any> |
||||
pull(input: pullInputType): Promise<any> |
||||
push(input: pushInputType): Promise<any> |
||||
currentbranch(input?: currentBranchInput): Promise<branch> |
||||
branch(input: branchInputType): Promise<void> |
||||
checkout(input: checkoutInputType): Promise<void> |
||||
add(input: addInputType): Promise<void> |
||||
rm(input: rmInputType): Promise<void> |
||||
resolveref(input: resolveRefInput): Promise<string> |
||||
readblob(input: readBlobInput): Promise<ReadBlobResult> |
||||
commit(input: commitInputType): Promise<string> |
||||
addremote(input: remote): Promise<void> |
||||
delremote(input: remote): Promise<void> |
||||
status(input?: statusInput): Promise<Array<StatusRow>> |
||||
compareBranches(input: compareBranchesInput): Promise<branchDifference> |
||||
init(input?: initInputType): Promise<void> |
||||
updateSubmodules: (input: updateSubmodulesInput) => Promise<void> |
||||
version: () => Promise<string> |
||||
} |
||||
} |
||||
|
@ -0,0 +1,10 @@ |
||||
import { ITerminal } from "@remixproject/plugin-api/src/lib/terminal" |
||||
import { StatusEvents } from "@remixproject/plugin-utils" |
||||
|
||||
export interface IExtendedTerminalApi extends ITerminal { |
||||
events: { |
||||
} & StatusEvents |
||||
methods: ITerminal['methods'] & { |
||||
logHtml(html: string): void |
||||
} |
||||
} |
@ -0,0 +1,206 @@ |
||||
import { Endpoints } from "@octokit/types" |
||||
import { AuthCallback, HttpClient, ReadCommitResult } from "isomorphic-git" |
||||
|
||||
export type branchDifference = { |
||||
uniqueHeadCommits: ReadCommitResult[], |
||||
uniqueRemoteCommits: ReadCommitResult[], |
||||
} |
||||
|
||||
|
||||
export type commitChange = { |
||||
type: commitChangeType |
||||
path: string, |
||||
hashModified: string, |
||||
hashOriginal: string, |
||||
original?: string, |
||||
modified?: string, |
||||
readonly?: boolean |
||||
} |
||||
|
||||
export type commitChangeTypes = { |
||||
"deleted": "D" |
||||
"modified": "M" |
||||
"added": "A", |
||||
"unknown": "?" |
||||
} |
||||
|
||||
export type pagedCommits = { |
||||
page: number, |
||||
perPage: number, |
||||
total: number, |
||||
hasNextPage: boolean, |
||||
commits: ReadCommitResult[] |
||||
} |
||||
|
||||
export enum syncStatus { |
||||
"sync" = "sync", |
||||
"publishBranch" = "publishBranch", |
||||
"none" = "none", |
||||
} |
||||
|
||||
export type repository = { |
||||
name: string |
||||
html_url: string |
||||
owner: { |
||||
login: string |
||||
}, |
||||
full_name: string |
||||
default_branch: string |
||||
id: number |
||||
url: string |
||||
} |
||||
|
||||
export type branch = { |
||||
name: string |
||||
remote: remote |
||||
} |
||||
|
||||
export type remote = { |
||||
name: string |
||||
url: string |
||||
} |
||||
|
||||
export type remoteBranch = { |
||||
name: string |
||||
} |
||||
|
||||
export type commitChangeType = keyof commitChangeTypes |
||||
|
||||
export type initInputType = { |
||||
defaultBranch: string |
||||
} |
||||
|
||||
export type author = { |
||||
name: string, |
||||
email: string, |
||||
} |
||||
|
||||
export type updateSubmodulesInput = { |
||||
dir?: string |
||||
token?: string |
||||
} |
||||
|
||||
export type remoteCommitsInputType = { |
||||
owner: string, repo: string, token: string, branch: string, length: number, page: number |
||||
} |
||||
|
||||
export type compareBranchesInput = { |
||||
branch: branch, remote: remote |
||||
} |
||||
|
||||
export type fetchInputType = { |
||||
remote: remote, |
||||
ref?: branch, |
||||
remoteRef?: branch, |
||||
depth?: number, |
||||
singleBranch?: boolean, |
||||
relative?: boolean, |
||||
quiet?: boolean |
||||
author?: author |
||||
token?: string |
||||
} |
||||
|
||||
export type logInputType = { |
||||
ref: string, |
||||
depth?: number, |
||||
} |
||||
|
||||
export type pullInputType = { |
||||
remote: remote,
|
||||
ref: branch,
|
||||
remoteRef?: branch |
||||
author?: author |
||||
token?: string |
||||
} |
||||
|
||||
export type pushInputType = { |
||||
remote: remote,
|
||||
ref: branch,
|
||||
remoteRef?: branch,
|
||||
force?: boolean, |
||||
author?: author, |
||||
token?: string |
||||
} |
||||
|
||||
export type branchInputType = { |
||||
ref: string, |
||||
checkout?: boolean |
||||
refresh?: boolean |
||||
force?: boolean |
||||
} |
||||
|
||||
export type currentBranchInput = { |
||||
fs: any, |
||||
dir: string |
||||
} |
||||
|
||||
export type checkoutInputType = { |
||||
ref: string, |
||||
force?: boolean, |
||||
remote?: string |
||||
refresh?: boolean |
||||
fetch?: boolean |
||||
} |
||||
|
||||
export type addInputType = { |
||||
filepath: string | string[] |
||||
} |
||||
|
||||
export type rmInputType = { |
||||
filepath: string |
||||
} |
||||
|
||||
export type resolveRefInput = { |
||||
ref: string |
||||
} |
||||
|
||||
export type readBlobInput = { |
||||
oid: string, |
||||
filepath: string |
||||
} |
||||
|
||||
export type commitInputType = { |
||||
author: { |
||||
name: string, |
||||
email: string, |
||||
}, |
||||
message: string, |
||||
} |
||||
|
||||
export type branchesInputType = { |
||||
fs?: any |
||||
dir?: string |
||||
} |
||||
|
||||
export interface cloneInputType { |
||||
url: string, |
||||
branch?: string, |
||||
depth?: number, |
||||
singleBranch?: boolean |
||||
workspaceName?: string |
||||
workspaceExists?: boolean |
||||
token?: string |
||||
dir?: string // where the clone should happen on desktop
|
||||
} |
||||
|
||||
export interface repositoriesInput { token: string, page?: number, per_page?: number } |
||||
|
||||
export interface statusInput { ref: string, filepaths?: string[] } |
||||
|
||||
export type isoGitFSConfig = { |
||||
fs: any, |
||||
dir: string, |
||||
} |
||||
|
||||
export type isoGitProxyConfig = { |
||||
corsProxy: string |
||||
http: HttpClient |
||||
onAuth: AuthCallback |
||||
} |
||||
|
||||
export type GitHubUser = Partial<Endpoints["GET /user"]["response"]['data']> & { |
||||
isConnected: boolean |
||||
} |
||||
|
||||
export type userEmails = Endpoints["GET /user/emails"]["response"]["data"] |
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue