Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | 1x 9x 9x 9x 9x 9x 81x 72x 9x 1x 1x 1x 3x 1x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 1x 1x 1x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import 'core-js/modules/web.url' import logger from '@wdio/logger' import NetworkHandler from './handler/network' import { DEFAULT_TRACING_CATEGORIES } from './constants' import { readIOStream, sumByKey } from './utils' const log = logger('@wdio/devtools-service:CommandHandler') export default class CommandHandler { constructor (client, browser) { this.client = client this.browser = browser this.isTracing = false this.networkHandler = new NetworkHandler(client) /** * register browser commands */ const commands = Object.getOwnPropertyNames(Object.getPrototypeOf(this)).filter( fnName => fnName !== 'constructor' && !fnName.startsWith('_')) commands.forEach(fnName => this.browser.addCommand(fnName, ::this[fnName])) /** * propagate CDP events to the browser event listener */ this.client.on('event', (event) => { const method = event.method || 'event' log.debug(`cdp event: ${method} with params ${JSON.stringify(event.params)}`) this.browser.emit(method, event.params) }) } /** * allow to easily access the CDP from the browser object */ cdp (domain, command, args = {}) { if (!this.client[domain]) { throw new Error(`Domain "${domain}" doesn't exist in the Chrome DevTools protocol`) } if (!this.client[domain][command]) { throw new Error(`The "${domain}" domain doesn't have a method called "${command}"`) } log.info(`Send command "${domain}.${command}" with args: ${JSON.stringify(args)}`) return new Promise((resolve, reject) => this.client[domain][command](args, (err, result) => { /* istanbul ignore if */ if (err) { return reject(new Error(`Chrome DevTools Error: ${result.message}`)) } return resolve(result) })) } /** * helper method to receive Chrome remote debugging connection data to * e.g. use external tools like lighthouse */ cdpConnection () { const { host, port } = this.client return { host, port } } /** * get nodeId to use for other commands */ async getNodeId (selector) { const document = await this.cdp('DOM', 'getDocument') const { nodeId } = await this.cdp( 'DOM', 'querySelector', { nodeId: document.root.nodeId, selector } ) return nodeId } /** * get nodeIds to use for other commands */ async getNodeIds (selector) { const document = await this.cdp('DOM', 'getDocument') const { nodeIds } = await this.cdp( 'DOM', 'querySelectorAll', { nodeId: document.root.nodeId, selector } ) return nodeIds } /** * start tracing the browser * * @param {string[]} [categories=DEFAULT_TRACING_CATEGORIES] categories to trace for * @param {Number} [samplingFrequency=10000] sampling frequency */ startTracing (categories = DEFAULT_TRACING_CATEGORIES, samplingFrequency = 10000) { if (this.isTracing) { throw new Error('browser is already being traced') } this.isTracing = true return this.cdp('Tracing', 'start', { categories: categories.join(','), transferMode: 'ReturnAsStream', options: `sampling-frequency=${samplingFrequency}` // 1000 is default and too slow. }) } /** * stop tracing the browser * * @return {Number} tracing id to use for other commands */ async endTracing () { if (!this.isTracing) { throw new Error('No tracing was initiated, call `browser.startTracing()` first') } this.cdp('Tracing', 'end') const stream = await new Promise((resolve, reject) => { const timeout = setTimeout( /* istanbul ignore next */ () => reject('Did not receive a Tracing.tracingComplete event'), 5000) this.browser.once('Tracing.tracingComplete', ({ stream }) => { clearTimeout(timeout) resolve(stream) this.isTracing = false }) }) this.traceEvents = await readIOStream(::this.cdp, stream) return stream } /** * get raw trace logs */ getTraceLogs () { return this.traceEvents } /** * get page weight from last page load */ getPageWeight () { const pageWeight = sumByKey(Object.values(this.networkHandler.requestTypes), 'size') const transferred = sumByKey(Object.values(this.networkHandler.requestTypes), 'encoded') const requestCount = sumByKey(Object.values(this.networkHandler.requestTypes), 'count') return { pageWeight, transferred, requestCount, details: this.networkHandler.requestTypes } } } |