import {gatherInfo} from './gatherInfo' import {doCheckIsBuildNeeded} from "./makecheck"; import {doBuild} from "./build" import {doNativeScript} from "./exportNS"; import {executeCommand} from "./execCmd" import * as path from 'path' import * as fs from 'fs' import * as ac from 'ansi-colors' import { networkInterfaces } from 'os' const TEST_ENABLED = false // TODO: This value should be portable to other modules // import {registerAppiumHandler, clientAppium} from "./appiumWSClient"; export function doTest() { if(!TEST_ENABLED) { console.log(ac.bold.red('TESTING IS NOT ENABLED in this Jove build')) return Promise.resolve() } console.log('setting up for test...') let p:any // console.log('running tests...') let {projPath, projName, buildFlags} = gatherInfo() let nsproject = path.resolve('..', 'nativescript', projName) const options = process.argv.slice(3) let appium = options.indexOf('appium') !== -1 let android = options.indexOf('android') !== -1 let debug = options.indexOf('debug') !== -1 let ios = options.indexOf('ios') !== -1 let deviceName = '' let ti = options.indexOf('device') if(ti !== -1) deviceName = options[ti+1] let platform = '' if(android) platform = 'android' else if(ios) platform = 'ios' let match= '' let mi = options.indexOf('match') if(mi !== -1) match=options[mi+1] if(!match) match = '**/*' let nativescript = !!platform const dtFile = nativescript ? path.resolve(nsproject, 'app', '~dotest') : path.resolve('build', '~dotest') const rpath = createCurrentReportFolder(platform) console.log('Report folder created:', rpath) if(buildFlags.clean || nativescript || doCheckIsBuildNeeded(projPath, projName)) { console.log('build first...') p = doBuild().then(() => { if(nativescript) { return doNativeScript() } }) } if(nativescript) { p = Promise.resolve(p).then(() => { console.log(ac.bold.green('--------------------------------------------------')) console.log(ac.bold.green(` Preparing for ${platform} testing`)) console.log(ac.italic.green(` Please be patient...`)) console.log(ac.bold.green('--------------------------------------------------')) return buildNativescript(projName, platform) }) } Promise.resolve(p).then(() => { // console.log('RUNNING TAP TEST SCRIPT (Server)') let matchset = './build/tests/'+match+'.test.js' p = executeCommand(`MATCH="${matchset}"; npm`, ['test'], '', true, {MATCH: matchset}).then((rt: any) => { if (rt.code) { console.log(ac.bold.red('Error'), ac.blue(rt.errStr)) } else { console.log('\n\n') console.log(ac.bold.blue('--------------------------------------------------')) console.log(ac.bold.blue(' Test Results')) console.log(ac.bold.blue('--------------------------------------------------')) let lines = rt.stdStr.split('\n') let errCode = rt.code for (let ln of lines) { ln = ln.trim() if (ln.length) { if (ln.charAt(0) === '>') continue if (ln.substring(0, 7) === './build') { console.log(ac.black.italic(ln)) } else if (ln.charAt(0) === '✓') { const cline = ln.substring(1).trim() if(cline.substring(0,13) === '>remoteTitle:') { const rttl = cline.substring(14).trim() console.log(ac.bold.magenta(' '+rttl)) } else { console.log(ac.bold.green(' ✓'), ac.green(cline)) } } else if (isFinite(Number(ln.charAt(0))) && ln.charAt(1) === ')') { errCode = 1 console.log(ac.bold.red(' x'), ac.red(ln)) } else { console.log(ac.bold.black(ln)) } } } // remove the test file fs.unlinkSync(dtFile) process.exit(errCode) } }) }) // console.log('>>>>>>>>>>>Determining how to run test build >>>>>>>>>>>>>>') // console.log('options specified', options) let contents = process.argv.slice(3).join(' ') // disposition (see app-core test handling) if(nativescript) { // write out host as part of dotest const host = getHostIP() contents += ' host='+host+'\n' } // write the ~dotest file out to signal a test fs.writeFileSync(dtFile,contents) // now run the app client we will test against const workingDirectoryOfOurApp = path.join(process.cwd(), 'build') const pathToOurApp = path.join(workingDirectoryOfOurApp, projName) console.log('waiting...') // Launch client return Promise.resolve(p).then(() => { setTimeout(() => { if(nativescript) { if(appium) { // start appium server p = runAppiumServer() // start appium target runner script p = p.then(() => { p = runAppiumTarget(deviceName, platform, nsproject, projName) }) } else { // launch via ns p = runNativescript(projName, platform, deviceName, debug) } } else { // run the electron app p = executeCommand(pathToOurApp, [], workingDirectoryOfOurApp, true) } Promise.resolve(p).then(() => { console.log(ac.blue.italic('application launched for testing ended unexpectedly')) fs.unlinkSync(dtFile) executeCommand('killall', ['node']) process.exit(1) }) }, (p !== undefined ? 5000 : 1)) // wait 5 seconds if we did a build to allow shell to clear out console.log('') }) } function buildNativescript(projName:string, platform:string) { let args = ['build', platform] let nsproject = path.resolve('..', 'nativescript', projName) console.log('>>>> Building ns '+ args.join(' ') +' from ', nsproject) return executeCommand('ns',args, nsproject,false) } function runNativescript(projName:string, platform:string, deviceName:string, debug:boolean):Promise { console.log(ac.bold.green('--------------------------------------------------------------')) console.log(ac.bold.green(`${platform} testing will commence shortly on device ${deviceName}...`)) console.log(ac.bold.green('--------------------------------------------------------------')) return new Promise(resolve => { const cmd = debug ? 'debug' : 'run' setTimeout(() => { let args = [cmd, platform, '--no-watch'] if(debug) args.push('--debug-brk') if (deviceName) { args.push('--device') args.push(deviceName) } let nsproject = path.resolve('..', 'nativescript', projName) console.log('>>>> Running ns ' + args.join(' ') + ' from ', nsproject) executeCommand('ns', args, nsproject, true).then(() => { resolve() }) }, 1) }) } function runAppiumServer() { console.log(ac.bold.grey.italic('>> Running appium server now...')) executeCommand('appium', [], '', false) return new Promise(resolve => {setTimeout(resolve, 2000)}) } function runAppiumTarget(deviceName:string, platform:string, nsproject:string, projName:string) { console.log(ac.bold('>> starting appium target script...')) const wdio = require("webdriverio"); // javascript const opts = { path: '/wd/hub', port: 4723, capabilities: { isHeadless: true, logcatFormat: "brief", platformName: "Android", platformVersion: "9", deviceName: "Android Emulator", avd: "medium", app: '', automationName: "UiAutomator2" } }; async function delay(ms:number) { return new Promise(resolve => {setTimeout(resolve, ms)}) } async function main () { const client = await wdio.remote(opts); // console.log('registering appium handler') // registerAppiumHandler((directive:string) => { // console.log('Appium sees directive ', directive) // let rt:any // if(directive === 'hello') { // rt = 'Well, hello to you too!' // } // if(directive === 'screenshot') { // rt = client.takeScreenshot() // } // return Promise.resolve(rt) // }) // console.log('starting appium listening client') // clientAppium('ws://localhost:51610').then(() => { // console.log('appium client connected, listening to handler') // }) } getNSDeviceInfo(nsproject, platform, deviceName).then((info:any) => { //app: "/Users/sohmert/tbd/puppet-test-ws/nativescript/platforms/android/app/build/outputs/apk/debug/app-debug.apk", // /Users/sohmert/tbd/nativescript/jove-test/platforms/ios/build/Debug-iphonesimulator/jovetest.app const apkPath = path.resolve(nsproject, 'platforms', 'android', 'app', 'build', 'outputs', 'apk', 'debug', 'app-debug.apk') const iosname = projName.split('-').join('') + '.app' const iosPath = path.resolve(nsproject, 'platforms', 'ios', 'build', 'Debug-iphonesimulator', iosname) if(platform === 'android') { opts.capabilities.platformName = "Android" opts.capabilities.platformVersion = info.platVer opts.capabilities.app = apkPath opts.capabilities.deviceName = "Android Emulator" opts.capabilities.avd = info.id opts.capabilities.automationName = "UiAutomator2" } if(platform === 'ios') { opts.capabilities.platformName = "iOS" opts.capabilities.platformVersion = info.platVer opts.capabilities.app = iosPath opts.capabilities.deviceName = deviceName opts.capabilities.automationName = "XCUITest" } return main(); }) } async function getNSDeviceInfo(nsproject:string, platform:string, deviceName:string) { return executeCommand('ns', ['devices', platform, '--available-devices'], nsproject).then((rt:any)=> { if(rt.code) { console.error(ac.red.bold(`Failed to enumerate available ${platform} device options`), ac.blue(rt.errStr)) } else { //│ medium │ Android │ 9.0.0 │ emulator-5554 │ medium │ │ //│ medium │ iOS │ 15.0 │ C952149A-CA8C-4D6A-9BEF-83E733A5A438 │ C952149A-CA8C-4D6A-9BEF-83E733A5A438 │ │ let NAME=1, PLAT=2, VER=3, ID=4, NAME2=5 let lines = rt.stdStr.split('\n') for(let ln of lines) { const col = ln.split('│') if(col.length > NAME2) { let name = col[NAME].trim() let plat = col[PLAT].trim().toLowerCase() let platVer = col[VER].trim() if (plat === platform && name === deviceName) { console.log(ac.green.dim.italic(ln)) return { id: col[NAME2].trim() || name, platVer: platVer } } } } console.error(ac.red.bold(`device ${deviceName} not found among available ${platform} device options`)) return {} } }) } function getHostIP() { const nets = networkInterfaces() const results = Object.create(null); // Or just '{}', an empty object for (const name of Object.keys(nets)) { // @ts-ignore for (const net of nets[name]) { // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses if (net.family === 'IPv4' && !net.internal) { if (!results[name]) { results[name] = []; } results[name].push(net.address); } } } // console.log('interfaces', results) let iface for (let nm of Object.getOwnPropertyNames(results)) { iface = results[nm] break; } const ipAddr = (iface && iface[0]) || 'localhost' return ipAddr } function createCurrentReportFolder(jplat:string) { const month = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'] let dt = new Date() let rplat = 'electron' if(jplat === 'android' || jplat === 'ios') rplat = 'mobile' let nm = `${month[dt.getMonth()]}-${dt.getDate()}` const rootPath = path.resolve('.') let cpth:string = path.join(rootPath, 'report', nm) // console.log("TEST REPORT ROOT PATH", rootPath) if(fs.existsSync(path.join(rootPath, 'package.json'))) { let ordinal = 0 let cpth:string = path.join(rootPath, 'report', nm) while (++ordinal) { cpth = path.join(rootPath, 'report', nm, '' + ordinal) if (!fs.existsSync(cpth)) { break; } } const folderPath = path.join(cpth, rplat) fs.mkdirSync(folderPath, {recursive:true}) let lnpth = path.join(rootPath, 'report', 'latest') if(fs.existsSync(lnpth)) fs.unlinkSync(lnpth) fs.symlinkSync(folderPath, lnpth) return folderPath } else { console.error('TEST REPORT: Root path not detected at ', rootPath) } }