Skip to content

Commit 08b0339

Browse files
collect console.logs
1 parent d353070 commit 08b0339

File tree

12 files changed

+176
-29
lines changed

12 files changed

+176
-29
lines changed

packages/app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"dependencies": {
1313
"@codemirror/lang-javascript": "^6.2.1",
1414
"@codemirror/theme-one-dark": "^6.1.2",
15+
"@codemirror/view": "^6.22.0",
1516
"@devtools/hook": "workspace:^",
1617
"@iconify-json/mdi": "^1.1.54",
1718
"@lit/context": "^1.0.1",

packages/app/src/components/workbench.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import './tabs.js'
1313
import './workbench/source.js'
1414
import './workbench/actions.js'
1515
import './workbench/logs.js'
16+
import './workbench/console.js'
1617
import './workbench/metadata.js'
1718
import './browser/snapshot.js'
1819

@@ -125,8 +126,8 @@ export class DevtoolsWorkbench extends Element {
125126
<wdio-devtools-tab label="Log">
126127
<wdio-devtools-logs></wdio-devtools-logs>
127128
</wdio-devtools-tab>
128-
<wdio-devtools-tab label="Console">
129-
Console tab not yet implemented!
129+
<wdio-devtools-tab label="Console" active>
130+
<wdio-devtools-console-logs></wdio-devtools-console-logs>
130131
</wdio-devtools-tab>
131132
<wdio-devtools-tab label="Network">
132133
Network tab not yet implemented!

packages/app/src/components/workbench/actions.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class DevtoolsActions extends Element {
4545
return this.#entries.map((entry, i) => {
4646
if ('command' in entry) {
4747
return html`
48-
<button>${entry.command}</button>
48+
<button @click="${() => this.#highlightLine(entry.callSource)}">${entry.command}</button>
4949
`
5050
}
5151

@@ -90,6 +90,13 @@ export class DevtoolsActions extends Element {
9090
`
9191
}
9292

93+
#highlightLine(callSource: string) {
94+
const event = new CustomEvent('app-source-highlight', {
95+
detail: callSource
96+
})
97+
window.dispatchEvent(event)
98+
}
99+
93100
#selectMutation(i: number) {
94101
this.#activeEntry = i
95102
const event = new CustomEvent('app-mutation-select', {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Element } from '@core/element'
2+
import { html, css } from 'lit'
3+
import { customElement } from 'lit/decorators.js'
4+
import { consume } from '@lit/context'
5+
6+
import { context, type TraceLog } from '../../context.js'
7+
8+
import '~icons/mdi/chevron-right.js'
9+
10+
const BG: Record<ConsoleLogs['type'], string> = {
11+
'warn': 'editorOverviewRulerWarningForeground',
12+
'info': 'editorOverviewRulerInfoForeground',
13+
'error': 'editorOverviewRulerErrorForeground',
14+
'log': 'panelBorder'
15+
}
16+
17+
const SOURCE_COMPONENT = 'wdio-devtools-console-logs'
18+
@customElement(SOURCE_COMPONENT)
19+
export class DevtoolsConsoleLogs extends Element {
20+
static styles = [...Element.styles, css`
21+
:host {
22+
display: flex;
23+
width: 100%;
24+
height: 100%;
25+
flex-direction: column;
26+
padding: 5px;
27+
}
28+
`]
29+
30+
@consume({ context })
31+
data: TraceLog = {} as TraceLog
32+
33+
render() {
34+
return html`
35+
${Object.values(this.data.consoleLogs).map((log) => html`
36+
<dl class="w-full flex grow-0">
37+
<dt class="flex">
38+
<icon-mdi-chevron-right class="text-base transition-transform block"></icon-mdi-chevron-right>
39+
<span class="block bg-${BG[log.type]} rounded text-sm py-[1px] px-[5px] my-1">${log.type}</span>
40+
</dt>
41+
<dd class="flex justify-center items-center mx-2">${log.args}</dd>
42+
</dl>
43+
`)}
44+
`
45+
}
46+
}
47+
48+
declare global {
49+
interface HTMLElementTagNameMap {
50+
[SOURCE_COMPONENT]: DevtoolsConsoleLogs
51+
}
52+
}

packages/app/src/components/workbench/source.ts

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { Element } from '@core/element'
22
import { html, css } from 'lit'
33
import { customElement } from 'lit/decorators.js'
4+
import { consume } from '@lit/context'
45

56
import { EditorView, basicSetup } from 'codemirror'
7+
import type { EditorViewConfig } from '@codemirror/view'
68
import { javascript } from '@codemirror/lang-javascript'
79
import { oneDark } from '@codemirror/theme-one-dark'
810

9-
const doc = `function greet(who) {
10-
return "Hello, " + who;
11-
}`
11+
import { context, type TraceLog } from '../../context.js'
1212

1313
const SOURCE_COMPONENT = 'wdio-devtools-source'
1414
@customElement(SOURCE_COMPONENT)
@@ -29,20 +29,49 @@ export class DevtoolsSource extends Element {
2929
}
3030
`]
3131

32+
@consume({ context })
33+
data: TraceLog = {} as TraceLog
34+
3235
connectedCallback(): void {
3336
super.connectedCallback()
34-
setTimeout(() => {
35-
const container = this.shadowRoot?.querySelector('section')
36-
if (!container) {
37-
return
38-
}
39-
const editorView = new EditorView({
40-
root: this.shadowRoot!,
41-
extensions: [basicSetup, javascript(), oneDark],
42-
doc
43-
})
44-
container.replaceWith(editorView.dom)
45-
})
37+
window.addEventListener('app-source-highlight', this.#highlightCallSource.bind(this))
38+
setTimeout(() => this.#renderEditor(Object.keys(this.data.sources)[0]))
39+
}
40+
41+
#renderEditor (filePath: string, highlightLine?: number) {
42+
const source = this.data.sources[filePath]
43+
if (!source) {
44+
return
45+
}
46+
47+
const container = this.shadowRoot?.querySelector('section') || this.shadowRoot?.querySelector('.cm-editor')
48+
if (!container) {
49+
return
50+
}
51+
52+
const opts: EditorViewConfig = {
53+
root: this.shadowRoot!,
54+
extensions: [basicSetup, javascript(), oneDark],
55+
doc: source,
56+
selection: { anchor: 4 }
57+
}
58+
const editorView = new EditorView(opts)
59+
container.replaceWith(editorView.dom)
60+
61+
/**
62+
* highlight line of call source
63+
*/
64+
const lines = [...(this.shadowRoot?.querySelectorAll('.cm-line') || [])]
65+
if (highlightLine && lines.length && highlightLine < lines.length) {
66+
setTimeout(() => {
67+
lines[highlightLine].classList.add('cm-activeLine')
68+
}, 100)
69+
}
70+
}
71+
72+
#highlightCallSource (ev: CustomEvent<string>) {
73+
const [filePath, line] = ev.detail.split(':')
74+
this.#renderEditor(filePath, parseInt(line, 10) + 1)
4675
}
4776

4877
render() {

packages/app/src/vite-env.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
interface GlobalEventHandlersEventMap {
44
'app-mutation-highlight': CustomEvent<TraceMutation>
5+
'app-source-highlight': CustomEvent<string>
56
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
67
'app-test-filter': CustomEvent<import('./components/sidebar/filter').DevtoolsSidebarFilter>
78
'app-logs': CustomEvent<string>

packages/hook/src/index.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ import { type CommandLog, type TraceLog, TraceType } from './types.js'
1414

1515
let commandsLog: CommandLog[] = []
1616
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+
}
1723

1824
export function setupForDevtools (opts: Options.WebdriverIO) {
1925
/**
@@ -39,16 +45,30 @@ export function setupForDevtools (opts: Options.WebdriverIO) {
3945
*/
4046
if (command === 'deleteSession') {
4147
await this.pause(1000)
42-
await captureTrace(this, command as keyof WebDriverCommands, [])
48+
const browser = getBrowserObject(this)
49+
await captureTrace(browser)
4350
}
4451
})
4552

4653
opts.afterCommand = Array.isArray(opts.afterCommand)
4754
? opts.afterCommand
4855
: opts.afterCommand ? [opts.afterCommand] : []
4956
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+
5069
if (PAGE_TRANSITION_COMMANDS.includes(command)) {
51-
await captureTrace(this, command as keyof WebDriverCommands, args, result, error)
70+
const browser = getBrowserObject(this)
71+
await captureTrace(browser)
5272
}
5373
})
5474

@@ -75,39 +95,44 @@ async function injectScript (browser: WebdriverIO.Browser) {
7595
})
7696
}
7797

78-
async function captureTrace (browser: WebdriverIO.Browser, command: (keyof WebDriverCommands), args: any, result?: any, error?: Error) {
79-
const timestamp = Date.now()
80-
98+
async function captureTrace (browser: WebdriverIO.Browser) {
8199
/**
82100
* only capture trace if script was injected and command is a page transition command
83101
*/
84102
if (!isInjected) {
85103
return
86104
}
87105

88-
const [mutations, logs, pageMetadata] = await browser.execute(() => [
106+
const [mutations, logs, pageMetadata, consoleLogs] = await browser.execute(() => [
89107
window.wdioDOMChanges,
90108
window.wdioTraceLogs,
91-
window.wdioMetadata
109+
window.wdioMetadata,
110+
window.wdioConsoleLogs
92111
])
93112

113+
if (!currentTraceId) {
114+
currentTraceId = pageMetadata.id
115+
}
116+
94117
if (currentTraceId !== pageMetadata.id) {
95118
commandsLog = []
119+
sources = new Map()
96120
}
97121

98-
commandsLog.push({ command, args, result, error, timestamp })
99122
const outputDir = browser.options.outputDir || process.cwd()
100123
const { capabilities, ...options } = browser.options as Options.WebdriverIO
101124
const traceLog: TraceLog = {
102125
mutations,
103126
logs,
127+
consoleLogs,
104128
metadata: {
105129
type: TraceType.Standalone,
106130
...pageMetadata,
107131
options,
108132
capabilities
109133
},
110-
commands: commandsLog
134+
commands: commandsLog,
135+
sources: Object.fromEntries(sources)
111136
}
112137
await fs.writeFile(path.join(outputDir, `${pageMetadata.id}.json`), JSON.stringify(traceLog))
113138
}

packages/hook/src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export interface CommandLog {
77
result: any
88
error?: Error
99
timestamp: number
10+
callSource: string
1011
}
1112

1213
export enum TraceType {
@@ -17,6 +18,7 @@ export enum TraceType {
1718
export interface TraceLog {
1819
mutations: TraceMutation[]
1920
logs: string[]
21+
consoleLogs: ConsoleLogs[]
2022
metadata: {
2123
type: TraceType
2224
id: string
@@ -25,5 +27,6 @@ export interface TraceLog {
2527
capabilities: Capabilities.RemoteCapability
2628
viewport: VisualViewport
2729
}
28-
commands: CommandLog[]
30+
commands: CommandLog[],
31+
sources: Record<string, string>
2932
}

packages/script/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { waitForBody, parseFragment, parseDocument, log, getRef, assignRef } from './utils.js'
1+
import { waitForBody, parseFragment, parseDocument, log, getRef, assignRef, patchConsoleObject } from './utils.js'
22

33
window.wdioCaptureErrors = []
44
window.wdioDOMChanges = []
5+
window.wdioConsoleLogs = []
56
window.wdioMetadata = {
67
url: window.location.href,
78
id: `wdio-trace-${Math.random().toString().slice(2)}`,
@@ -13,6 +14,8 @@ try {
1314
await waitForBody()
1415
log('body rendered')
1516

17+
patchConsoleObject()
18+
1619
assignRef(document.documentElement)
1720
log('applied wdio ref ids')
1821

packages/script/src/utils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,18 @@ export function getRef (elem: Node) {
109109
}
110110
return (elem as Element).getAttribute('data-wdio-ref')
111111
}
112+
113+
const consoleMethods = ['log', 'info', 'warn', 'error'] as const
114+
export function patchConsoleObject () {
115+
consoleMethods.forEach((type: (typeof consoleMethods)[number]) => {
116+
const orig = console[type]
117+
console[type] = (...args) => {
118+
window.wdioConsoleLogs.push({
119+
timestamp: Date.now(),
120+
type,
121+
args
122+
})
123+
return orig(...args)
124+
}
125+
})
126+
}

0 commit comments

Comments
 (0)