import { performance, PerformanceObserver } from 'node:perf_hooks'
import { promisify } from 'node:util'
import testingbotTunnel from 'testingbot-tunnel-launcher'
import logger from '@wdio/logger'
import type { Capabilities, Options, Services } from '@wdio/types'
import type { TestingbotOptions, TestingbotTunnel, TunnelLauncherOptions } from './types.js'
const log = logger('@wdio/testingbot-service')
export default class TestingBotLauncher implements Services.ServiceInstance {
options: TestingbotOptions
tbTunnelOpts!: TunnelLauncherOptions
tunnel?: TestingbotTunnel
constructor (options: TestingbotOptions) {
this.options = options
}
async onPrepare (config: Options.Testrunner, capabilities: Capabilities.TestrunnerCapabilities) {
if (!this.options.tbTunnel || !config.user || !config.key) {
return
}
const tbTunnelIdentifier = (
this.options.tbTunnelOpts?.tunnelIdentifier ||
`TB-tunnel-${Math.random().toString().slice(2)}`
)
this.tbTunnelOpts = Object.assign({
apiKey: config.user,
apiSecret: config.key,
'tunnel-identifier': tbTunnelIdentifier,
}, this.options.tbTunnelOpts)
const capabilitiesEntries = Array.isArray(capabilities) ?
(capabilities as []).every(cap => Object.values(cap).length > 0 && Object.values(cap).every(c => typeof c === 'object' && (c as Capabilities.RequestedMultiremoteCapabilities).capabilities)) ?
capabilities.flatMap((cap: Capabilities.RequestedMultiremoteCapabilities ) => Object.values(cap))
: capabilities
: Object.values(capabilities)
for (const capability of capabilitiesEntries) {
const caps = (capability as Capabilities.WithRequestedCapabilities).capabilities || capability
const c = (caps as Capabilities.W3CCapabilities).alwaysMatch || caps
if (!c['tb:options']) {
c['tb:options'] = {}
}
c['tb:options']['tunnel-identifier'] = tbTunnelIdentifier
}
* measure TestingBot tunnel boot time
*/
const obs = new PerformanceObserver((list) => {
const entry = list.getEntries()[0]
log.info(`TestingBot tunnel successfully started after ${entry.duration}ms`)
})
obs.observe({ entryTypes: ['measure'] })
performance.mark('tbTunnelStart')
this.tunnel = await promisify(testingbotTunnel)(this.tbTunnelOpts)
performance.mark('tbTunnelEnd')
performance.measure('bootTime', 'tbTunnelStart', 'tbTunnelEnd')
}
* Shut down the tunnel
* @returns {Promise} Resolved promise when tunnel is closed
*/
onComplete () {
if (!this.tunnel) {
return
}
return new Promise(resolve => this.tunnel!.close(resolve))
}
}