11/// <reference types="../../script/types.d.ts" />
22
3- import fs from 'node:fs/promises'
4- import url from 'node:url'
5- import path from 'node:path'
6-
7- import { resolve } from 'import-meta-resolve'
8- import { SevereServiceError } from 'webdriverio'
93import type { Capabilities , Options } from '@wdio/types'
104import type { WebDriverCommands } from '@wdio/protocols'
115
12- import { PAGE_TRANSITION_COMMANDS } from './constants.js'
13- import { type CommandLog , type TraceLog , TraceType } from './types.js'
14-
15- let commandsLog : CommandLog [ ] = [ ]
16- let currentTraceId : string | undefined
17- let sources = new Map < string , string > ( )
18-
19- function getBrowserObject ( elem : WebdriverIO . Element | WebdriverIO . Browser ) : WebdriverIO . Browser {
20- const elemObject = elem as WebdriverIO . Element
21- return ( elemObject as WebdriverIO . Element ) . parent ? getBrowserObject ( elemObject . parent ) : elem as WebdriverIO . Browser
22- }
6+ import { SessionCapturer } from './session.js'
237
248export function setupForDevtools ( opts : Options . WebdriverIO ) {
9+ const session = new SessionCapturer ( )
10+
2511 /**
2612 * make sure to run with Bidi enabled by setting `webSocketUrl` to `true`
2713 */
@@ -34,105 +20,26 @@ export function setupForDevtools (opts: Options.WebdriverIO) {
3420 : opts . capabilities as WebdriverIO . Capabilities
3521 caps . webSocketUrl = true
3622
23+ /**
24+ * register before command hook
25+ */
3726 opts . beforeCommand = Array . isArray ( opts . beforeCommand )
3827 ? opts . beforeCommand
3928 : opts . beforeCommand ? [ opts . beforeCommand ] : [ ]
40- opts . beforeCommand . push ( async function ( this : WebdriverIO . Browser , command ) {
41- await injectScript ( this )
42-
43- /**
44- * capture trace on `deleteSession` before command is called
45- */
46- if ( command === 'deleteSession' ) {
47- await this . pause ( 1000 )
48- const browser = getBrowserObject ( this )
49- await captureTrace ( browser )
50- }
29+ opts . beforeCommand . push ( async function ( this : WebdriverIO . Browser , command : keyof WebDriverCommands ) {
30+ return session . beforeCommand ( this , command )
5131 } )
5232
33+ /**
34+ * register after command hook
35+ */
5336 opts . afterCommand = Array . isArray ( opts . afterCommand )
5437 ? opts . afterCommand
5538 : opts . afterCommand ? [ opts . afterCommand ] : [ ]
56- opts . afterCommand . push ( async function ( this : WebdriverIO . Browser , command : keyof WebDriverCommands , args , result , error ) {
57- const timestamp = Date . now ( )
58- const callSource = ( new Error ( '' ) ) . stack ?. split ( '\n' ) . pop ( ) ?. split ( ' ' ) . pop ( ) !
59- const sourceFile = callSource . split ( ':' ) . slice ( 0 , - 2 ) . join ( ':' )
60- const absPath = sourceFile . startsWith ( 'file://' )
61- ? url . fileURLToPath ( sourceFile )
62- : sourceFile
63- if ( sourceFile && ! sources . has ( sourceFile ) ) {
64- const sourceCode = await fs . readFile ( absPath , 'utf-8' )
65- sources . set ( absPath , sourceCode . toString ( ) )
66- }
67- commandsLog . push ( { command, args, result, error, timestamp, callSource : absPath } )
68-
69- if ( PAGE_TRANSITION_COMMANDS . includes ( command ) ) {
70- const browser = getBrowserObject ( this )
71- await captureTrace ( browser )
72- }
73- } )
74-
75- return opts
76- }
77-
78- let isInjected = false
79- async function injectScript ( browser : WebdriverIO . Browser ) {
80- if ( isInjected ) {
81- return
82- }
83-
84- if ( ! browser . isBidi ) {
85- throw new SevereServiceError ( `Can not set up devtools for session with id "${ browser . sessionId } " because it doesn't support WebDriver Bidi` )
86- }
39+ opts . afterCommand . push ( session . afterCommand . bind ( session ) )
8740
88- isInjected = true
89- const script = await resolve ( '@devtools/script' , import . meta. url )
90- const source = ( await fs . readFile ( url . fileURLToPath ( script ) ) ) . toString ( )
91- const functionDeclaration = `async () => { ${ source } }`
92-
93- await browser . scriptAddPreloadScriptCommand ( {
94- functionDeclaration
95- } )
96- }
97-
98- async function captureTrace ( browser : WebdriverIO . Browser ) {
9941 /**
100- * only capture trace if script was injected and command is a page transition command
42+ * return modified session configuration
10143 */
102- if ( ! isInjected ) {
103- return
104- }
105-
106- const [ mutations , logs , pageMetadata , consoleLogs ] = await browser . execute ( ( ) => [
107- window . wdioDOMChanges ,
108- window . wdioTraceLogs ,
109- window . wdioMetadata ,
110- window . wdioConsoleLogs
111- ] )
112-
113- if ( ! currentTraceId ) {
114- currentTraceId = pageMetadata . id
115- }
116-
117- if ( currentTraceId !== pageMetadata . id ) {
118- commandsLog = [ ]
119- sources = new Map ( )
120- }
121-
122- const outputDir = browser . options . outputDir || process . cwd ( )
123- const { capabilities, ...options } = browser . options as Options . WebdriverIO
124- const traceLog : TraceLog = {
125- mutations,
126- logs,
127- consoleLogs,
128- metadata : {
129- type : TraceType . Standalone ,
130- ...pageMetadata ,
131- options,
132- capabilities
133- } ,
134- commands : commandsLog ,
135- sources : Object . fromEntries ( sources )
136- }
137- await fs . writeFile ( path . join ( outputDir , `${ pageMetadata . id } .json` ) , JSON . stringify ( traceLog ) )
44+ return opts
13845}
0 commit comments