Skip to content

Commit 6f22ab6

Browse files
make rerender of page happen
1 parent 9e82ccc commit 6f22ab6

File tree

7 files changed

+180
-31
lines changed

7 files changed

+180
-31
lines changed

packages/app/src/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export class WebdriverIODevtoolsApplication extends Element {
7575
<section class="flex h-[calc(100%-40px)] w-full relative">
7676
${
7777
// only render sidebar if trace file is captured using testrunner
78-
this.data?.metadata.type === 'standalone'
78+
this.data?.metadata.type !== 'standalone'
7979
? html`<wdio-devtools-sidebar style="${this.#drag.getPosition()}"></wdio-devtools-sidebar>`
8080
: nothing
8181
}

packages/app/src/components/browser/snapshot.ts

Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,17 @@ function transform (node: any): VNode<{}> {
1717
}
1818

1919
const { children, ...props } = node.props
20+
/**
21+
* ToDo(Christian): fix way we collect data on added nodes in script
22+
*/
23+
if (!node.type && children.type) {
24+
return transform(children)
25+
}
26+
2027
const childrenRequired = children || []
21-
const c = Array.isArray(childrenRequired) ? childrenRequired : [childrenRequired]
28+
const c = Array.isArray(childrenRequired)
29+
? childrenRequired
30+
: [childrenRequired]
2231
return h(node.type as string, props, ...c.map(transform)) as VNode<{}>
2332
}
2433

@@ -63,9 +72,14 @@ export class DevtoolsBrowser extends Element {
6372
window.addEventListener('resize', this.#setIframeSize.bind(this))
6473
window.addEventListener('window-drag', this.#setIframeSize.bind(this))
6574
window.addEventListener('app-mutation-highlight', this.#highlightMutation.bind(this))
75+
window.addEventListener('app-mutation-select', (ev) => this.#renderBrowserState(ev.detail))
6676
await this.updateComplete
6777
this.#setIframeSize()
68-
this.#handleMutation(this.data.mutations[0])
78+
79+
/**
80+
* Render initial document
81+
*/
82+
this.#renderBrowserState(0)
6983
}
7084

7185
#setIframeSize () {
@@ -117,7 +131,6 @@ export class DevtoolsBrowser extends Element {
117131
docChildren.unshift(head)
118132
}
119133
render(root, this.#vdom)
120-
this.#renderVdom()
121134
}
122135

123136
#renderVdom () {
@@ -131,7 +144,6 @@ export class DevtoolsBrowser extends Element {
131144
* representation of the page
132145
*/
133146
[...this.#vdom.querySelectorAll('script')].forEach((el) => el.remove())
134-
135147
docEl.ownerDocument.replaceChild(this.#vdom, docEl)
136148
}
137149

@@ -140,27 +152,87 @@ export class DevtoolsBrowser extends Element {
140152
await this.updateComplete
141153
}
142154

143-
const hasRenderedFrame = this.iframe?.contentDocument?.documentElement
144-
.querySelectorAll('*').length === 2 // only body and head are in an empty iframe
145-
const doc = mutation.addedNodes[0]
146-
if (hasRenderedFrame && typeof doc !== 'string') {
147-
return this.#renderNewDocument(doc)
155+
if (mutation.type === 'attributes') {
156+
return this.#handleAttributeMutation(mutation)
157+
}
158+
if (mutation.type === 'childList') {
159+
return this.#handleChildListMutation(mutation)
160+
}
161+
if (mutation.type === 'characterData') {
162+
return this.#handleCharacterDataMutation(mutation)
163+
}
164+
}
165+
166+
#handleCharacterDataMutation (mutation: TraceMutation) {
167+
const el = this.#queryElement(mutation.target!)
168+
if (!el) {
169+
return
170+
}
171+
172+
el.textContent = mutation.newTextContent || ''
173+
}
174+
175+
#handleAttributeMutation (mutation: TraceMutation) {
176+
if (!mutation.attributeName || !mutation.attributeValue) {
177+
return
178+
}
179+
180+
const el = this.#queryElement(mutation.target!)
181+
if (!el) {
182+
return
148183
}
149184

150-
// TODO: handle mutations
185+
el.setAttribute(mutation.attributeName, mutation.attributeValue || '')
186+
}
187+
188+
#handleChildListMutation (mutation: TraceMutation) {
189+
if (mutation.addedNodes.length === 1 && !mutation.target) {
190+
this.#renderNewDocument(mutation.addedNodes[0] as SimplifiedVNode)
191+
return this.#renderVdom()
192+
}
193+
194+
const el = this.#queryElement(mutation.target!)
195+
if (!el) {
196+
return
197+
}
198+
199+
mutation.addedNodes.forEach((node) => {
200+
if (typeof node === 'string') {
201+
el.appendChild(document.createTextNode(node))
202+
} else {
203+
const root = transform(node)
204+
render(root, el)
205+
}
206+
})
207+
208+
mutation.removedNodes.forEach((ref) => {
209+
const child = this.#queryElement(ref, el)
210+
if (child) {
211+
child.remove()
212+
}
213+
})
214+
}
215+
216+
#queryElement (ref: string, el?: HTMLElement) {
217+
const rootElement = el || this.iframe?.contentDocument
218+
if (!rootElement) {
219+
return
220+
}
221+
return rootElement.querySelector(`*[data-wdio-ref="${ref}"]`) as HTMLElement
151222
}
152223

153224
#highlightMutation (ev: CustomEvent<TraceMutation>) {
154225
const mutation = ev.detail
155226
const docEl = this.iframe?.contentDocument
156-
if (!docEl) {
227+
if (!docEl || !mutation.target) {
157228
return
158229
}
159-
const el = docEl.querySelector(`*[data-wdio-ref="${mutation.target}"]`) as HTMLElement
230+
const el = this.#queryElement(mutation.target)
160231
if (!el) {
161232
return
162233
}
163234

235+
el.scrollIntoView({ block: 'center', inline: 'center' })
164236
const rect = el.getBoundingClientRect()
165237
const scrollY = this.iframe?.contentWindow?.scrollY || 0
166238
const scrollX = this.iframe?.contentWindow?.scrollX || 0
@@ -172,6 +244,24 @@ export class DevtoolsBrowser extends Element {
172244
docEl.body.appendChild(highlight)
173245
}
174246

247+
async #renderBrowserState (mutationIndex: number) {
248+
this.#vdom = document.createDocumentFragment()
249+
for (let i = 0; i <= mutationIndex; i++) {
250+
await this.#handleMutation(this.data.mutations[i])
251+
}
252+
253+
/**
254+
* scroll changed element into view
255+
*/
256+
const mutation = this.data.mutations[mutationIndex]
257+
if (mutation.target) {
258+
const el = this.#queryElement(mutation.target)
259+
if (el) {
260+
el.scrollIntoView({ block: 'center', inline: 'center' })
261+
}
262+
}
263+
}
264+
175265
render() {
176266
return html`
177267
<section class="w-full bg-sideBarBackground rounded-t-md shadow-md">

packages/app/src/components/workbench.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ export class DevtoolsWorkbench extends Element {
4141
#dragVertical = new DragController(this, {
4242
localStorageKey: 'toolbarHeight',
4343
minPosition: MIN_WORKBENCH_HEIGHT,
44-
initialPosition: window.innerHeight * .7, // initial height of sidebase is 20% of window
44+
initialPosition: window.innerHeight * .7, // initial height of browser window is 70% of window
4545
getContainerEl: () => this.getShadowRootAsync() as any as Element,
4646
direction: Direction.vertical
4747
})
4848

4949
#dragHorizontal = new DragController(this, {
5050
localStorageKey: 'workbenchSidebarWidth',
5151
minPosition: MIN_METATAB_WIDTH,
52-
initialPosition: MIN_METATAB_WIDTH, // initial height of sidebase is 20% of window
52+
initialPosition: MIN_METATAB_WIDTH,
5353
getContainerEl: () => this.#getHorizontalWindow(),
5454
direction: Direction.horizontal
5555
})
@@ -94,7 +94,7 @@ export class DevtoolsWorkbench extends Element {
9494
<wdio-devtools-tab label="Actions">
9595
<wdio-devtools-actions></wdio-devtools-actions>
9696
</wdio-devtools-tab>
97-
<wdio-devtools-tab label="Metadata" active>
97+
<wdio-devtools-tab label="Metadata">
9898
<wdio-devtools-metadata></wdio-devtools-metadata>
9999
</wdio-devtools-tab>
100100
<nav class="ml-auto" slot="actions">

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

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Element } from '@core/element'
2-
import { html, css } from 'lit'
2+
import { html, css, nothing } from 'lit'
33
import { customElement } from 'lit/decorators.js'
44
import { consume } from '@lit/context'
55
import type { CommandLog } from '@devtools/hook/types'
@@ -10,13 +10,17 @@ import '~icons/mdi/pencil.js'
1010
import '~icons/mdi/family-tree.js'
1111
import '~icons/mdi/alert.js'
1212
import '~icons/mdi/document.js'
13+
import '~icons/mdi/arrow-right.js'
1314

14-
const ICON_CLASS = 'w-[20px] h-[20px] m-1 mr-2 shrink-0'
15+
type ActionEntry = TraceMutation | CommandLog
1516

17+
const ONE_MINUTE = 1000 * 60
18+
const ICON_CLASS = 'w-[20px] h-[20px] m-1 mr-2 shrink-0 block'
1619
const SOURCE_COMPONENT = 'wdio-devtools-actions'
20+
1721
@customElement(SOURCE_COMPONENT)
1822
export class DevtoolsActions extends Element {
19-
#entries: (TraceMutation | CommandLog)[] = []
23+
#entries: ActionEntry[] = []
2024
#activeEntry?: number
2125
#highlightedMutation?: number
2226

@@ -28,6 +32,10 @@ export class DevtoolsActions extends Element {
2832
}
2933
`]
3034

35+
get mutationEntries () {
36+
return this.#entries.filter((entry) => !('command' in entry)) as TraceMutation[]
37+
}
38+
3139
@consume({ context })
3240
data: TraceLog = {} as TraceLog
3341

@@ -45,22 +53,42 @@ export class DevtoolsActions extends Element {
4553
return this.#entries.map((entry, i) => {
4654
if ('command' in entry) {
4755
return html`
48-
<button @click="${() => this.#highlightLine(entry.callSource)}">${entry.command}</button>
56+
<button class="flex px-2 items-center" @click="${() => this.#highlightLine(entry.callSource)}">
57+
<icon-mdi-arrow-right class="${ICON_CLASS}"></icon-mdi-arrow-right>
58+
${this.#renderTime(entry)}
59+
<code class="text-sm flex-wrap text-left break-all">${entry.command}(${entry.args.map((arg) => JSON.stringify(arg, null, 2)).join(', ')})</code>
60+
</button>
4961
`
5062
}
5163

5264
return html`
5365
<button
5466
@mousemove="${() => this.#showMutationTarget(i)}"
55-
@click="${() => this.#selectMutation(i)}"
56-
class="flex items-center justify-center text-sm w-full px-4 hover:bg-toolbarHoverBackground ${this.#activeEntry === i ? 'bg-toolbarHoverBackground' : ''}"
67+
@click="${() => this.#selectMutation(this.mutationEntries.indexOf(entry), i)}"
68+
class="flex items-center justify-center text-sm w-full px-2 hover:bg-toolbarHoverBackground ${this.#activeEntry === i ? 'bg-toolbarHoverBackground' : ''}"
5769
>
5870
${this.#getMutationLabel(entry)}
5971
</button>
6072
`
6173
})
6274
}
6375

76+
#renderTime (entry: ActionEntry) {
77+
const diff = entry.timestamp - this.data.mutations[0].timestamp
78+
let diffLabel = `${diff}ms`
79+
if (diff > 1000) {
80+
diffLabel = `${(diff / 1000).toFixed(2)}s`
81+
}
82+
if (diff > ONE_MINUTE) {
83+
const minutes = Math.floor(diff / 1000 / 60)
84+
diffLabel = `${minutes}m ${Math.floor((diff - minutes * ONE_MINUTE) / 1000)}s`
85+
}
86+
87+
return html`
88+
<span class="text-xs text-gray-500 shrink-0 pr-2 text-debugTokenExpressionName">${diffLabel}</span>
89+
`
90+
}
91+
6492
#getMutationLabel(mutation: TraceMutation) {
6593
if (mutation.type === 'attributes') {
6694
return this.#getAttributeMutationLabel(mutation)
@@ -73,35 +101,53 @@ export class DevtoolsActions extends Element {
73101
#getAttributeMutationLabel(mutation: TraceMutation) {
74102
return html`
75103
<icon-mdi-pencil class="${ICON_CLASS}"></icon-mdi-pencil>
76-
<span class="flex-grow">${mutation.target} attribute "<code>${mutation.attributeName}</code>" changed</span>
104+
${this.#renderTime(mutation)}
105+
<span class="flex-grow text-left">element attribute "<code>${mutation.attributeName}</code>" changed</span>
77106
`
78107
}
79108

80109
#getChildListMutationLabel(mutation: TraceMutation) {
81110
if (mutation.addedNodes.length === 1 && (mutation.addedNodes[0] as any).type === 'html') {
82111
return html`
83112
<icon-mdi-document class="${ICON_CLASS}"></icon-mdi-document>
84-
<span class="flex-grow">Document loaded</span>
113+
${this.#renderTime(mutation)}
114+
<span class="flex-grow text-left">Document loaded</span>
85115
`
86116
}
87117
return html`
88118
<icon-mdi-family-tree class="${ICON_CLASS}"></icon-mdi-family-tree>
89-
<span class="flex-grow">${mutation.target} child list changed</span>
119+
${this.#renderTime(mutation)}
120+
<span class="flex-grow text-left">
121+
${this.#renderNodeAmount(mutation.addedNodes, 'added')}
122+
${mutation.addedNodes.length && mutation.removedNodes.length
123+
? ' and '
124+
: nothing}
125+
${this.#renderNodeAmount(mutation.removedNodes, 'removed')}
126+
</span>
90127
`
91128
}
92129

130+
#renderNodeAmount (nodes: (string | SimplifiedVNode)[], operationType: 'added' | 'removed') {
131+
if (!nodes.length) {
132+
return nothing
133+
}
134+
let nodeLabel = 'node'
135+
if (nodes.length > 1) {
136+
nodeLabel = 'nodes'
137+
}
138+
return html`${nodes.length} ${nodeLabel} ${operationType}`
139+
}
140+
93141
#highlightLine(callSource: string) {
94142
const event = new CustomEvent('app-source-highlight', {
95143
detail: callSource
96144
})
97145
window.dispatchEvent(event)
98146
}
99147

100-
#selectMutation(i: number) {
101-
this.#activeEntry = i
102-
const event = new CustomEvent('app-mutation-select', {
103-
detail: this.#entries[this.#activeEntry]
104-
})
148+
#selectMutation(detail: number, listIndex: number) {
149+
this.#activeEntry = listIndex
150+
const event = new CustomEvent('app-mutation-select', { detail })
105151
window.dispatchEvent(event)
106152
this.requestUpdate()
107153
}

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-mutation-select': CustomEvent<number>
56
'app-source-highlight': CustomEvent<string>
67
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
78
'app-test-filter': CustomEvent<import('./components/sidebar/filter').DevtoolsSidebarFilter>

packages/script/src/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,20 @@ try {
4646
const previousSibling = ps ? getRef(ps) : null
4747
const nextSibling = ns ? getRef(ns) : null
4848

49+
let attributeValue: string | undefined
50+
if (type === 'attributes') {
51+
attributeValue = (t as Element).getAttribute(attributeName!) || ''
52+
}
53+
let newTextContent: string | undefined
54+
if (type === 'characterData') {
55+
newTextContent = (t as Element).textContent || ''
56+
}
57+
4958
log(`added mutation: ${type}`)
5059
return {
5160
type, attributeName, attributeNamespace, oldValue, addedNodes, target,
52-
removedNodes, previousSibling, nextSibling, timestamp
61+
removedNodes, previousSibling, nextSibling, timestamp, attributeValue,
62+
newTextContent
5363
} as TraceMutation
5464
}))
5565
} catch (err: any) {

packages/script/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ declare global {
3232
type: MutationRecordType
3333
attributeName?: string
3434
attributeNamespace?: string
35+
attributeValue?: string
36+
newTextContent?: string
3537
oldValue?: string
3638
addedNodes: (string | SimplifiedVNode)[]
3739
target?: string

0 commit comments

Comments
 (0)