Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions example/wdio.conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ export const config: Options.Testrunner = {
capabilities: [
{
browserName: 'chrome',
browserVersion: '144.0.7559.60', // specify chromium browser version for testing
browserVersion: '146.0.7680.72', // specify chromium browser version for testing
'goog:chromeOptions': {
args: [
'--headless',
'--disable-gpu',
'--remote-allow-origins=*',
'--window-size=1280,800'
'--window-size=1600,1200'
]
}
// }, {
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"scripts": {
"build": "pnpm --parallel build",
"demo": "wdio run ./example/wdio.conf.ts",
"demo:nightwatch": "pnpm --filter @wdio/nightwatch-devtools example",
"dev": "pnpm --parallel dev",
"preview": "pnpm --parallel preview",
"test": "vitest run",
Expand All @@ -17,7 +18,10 @@
"pnpm": {
"overrides": {
"vite": "^7.3.0"
}
},
"ignoredBuiltDependencies": [
"chromedriver"
]
},
"devDependencies": {
"@types/node": "^25.0.3",
Expand Down
116 changes: 87 additions & 29 deletions packages/app/src/components/browser/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
mutationContext,
type TraceMutation,
metadataContext,
type Metadata
type Metadata,
commandContext
} from '../../controller/DataManager.js'

import '~icons/mdi/world.js'
Expand All @@ -20,15 +21,16 @@ import '../placeholder.js'
const MUTATION_SELECTOR = '__mutation-highlight__'

function transform(node: any): VNode<{}> {
if (typeof node !== 'object') {
if (typeof node !== 'object' || node === null) {
// Plain string/number text node — return as-is for Preact to render as text.
return node as VNode<{}>
}

const { children, ...props } = node.props
const { children, ...props } = node.props ?? {}
/**
* ToDo(Christian): fix way we collect data on added nodes in script
*/
if (!node.type && children.type) {
if (!node.type && children?.type) {
return transform(children)
}

Expand All @@ -44,13 +46,18 @@ const COMPONENT = 'wdio-devtools-browser'
export class DevtoolsBrowser extends Element {
#vdom = document.createDocumentFragment()
#activeUrl?: string
/** Base64 PNG of the screenshot for the currently selected command, or null. */
#screenshotData: string | null = null

@consume({ context: metadataContext, subscribe: true })
metadata: Metadata | undefined = undefined

@consume({ context: mutationContext, subscribe: true })
mutations: TraceMutation[] = []

@consume({ context: commandContext, subscribe: true })
commands: CommandLog[] = []

static styles = [
...Element.styles,
css`
Expand Down Expand Up @@ -112,6 +119,31 @@ export class DevtoolsBrowser extends Element {
border-radius: 0 0 0.5rem 0.5rem;
min-height: 0;
}

.screenshot-overlay {
position: absolute;
inset: 0;
background: #111;
display: flex;
align-items: flex-start;
justify-content: center;
border-radius: 0 0 0.5rem 0.5rem;
overflow: hidden;
}

.screenshot-overlay img {
max-width: 100%;
height: auto;
display: block;
}

.iframe-wrapper {
position: relative;
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
`
]

Expand Down Expand Up @@ -148,9 +180,16 @@ export class DevtoolsBrowser extends Element {
return
}

// viewport may not be serialized yet (race between metadata message and
// first resize event), or may arrive without dimensions — fall back to
// sensible defaults so we never throw.
const viewportWidth = (metadata.viewport as any)?.width || 1280
const viewportHeight = (metadata.viewport as any)?.height || 800
if (!viewportWidth || !viewportHeight) {
return
}

this.iframe.removeAttribute('style')
const viewportWidth = metadata.viewport.width
const viewportHeight = metadata.viewport.height
const frameSize = this.getBoundingClientRect()
const headerSize = this.header.getBoundingClientRect()

Expand Down Expand Up @@ -178,23 +217,8 @@ export class DevtoolsBrowser extends Element {
)

async #renderCommandScreenshot(command?: CommandLog) {
const screenshot = command?.screenshot
if (!screenshot) {
return
}

if (!this.iframe) {
await this.updateComplete
}
if (!this.iframe) {
return
}

this.iframe.srcdoc = `
<body style="margin:0;background:#111;display:flex;justify-content:center;align-items:flex-start;">
<img src="data:image/png;base64,${screenshot}" style="max-width:100%;height:auto;display:block;" />
</body>
`
this.#screenshotData = command?.screenshot ?? null
this.requestUpdate()
}

async #renderNewDocument(doc: SimplifiedVNode, baseUrl: string) {
Expand Down Expand Up @@ -270,7 +294,11 @@ export class DevtoolsBrowser extends Element {

#handleChildListMutation(mutation: TraceMutation) {
if (mutation.addedNodes.length === 1 && !mutation.target) {
const baseUrl = this.metadata?.url || 'unknown'
// Prefer the URL embedded in the mutation itself (set by the injected script
// at capture time), then fall back to the already-resolved active URL, and
// finally to the context metadata URL. This avoids a race where metadata
// arrives after the first childList mutation fires #renderNewDocument.
const baseUrl = mutation.url || this.#activeUrl || this.metadata?.url || 'unknown'
this.#renderNewDocument(
mutation.addedNodes[0] as SimplifiedVNode,
baseUrl
Expand Down Expand Up @@ -389,6 +417,15 @@ export class DevtoolsBrowser extends Element {
this.requestUpdate()
}

/** Latest screenshot from any command — auto-updates the preview as tests run. */
get #latestAutoScreenshot(): string | null {
if (!this.commands?.length) return null
for (let i = this.commands.length - 1; i >= 0; i--) {
if (this.commands[i].screenshot) return this.commands[i].screenshot!
}
return null
}

render() {
/**
* render a browser state if it hasn't before
Expand All @@ -398,6 +435,10 @@ export class DevtoolsBrowser extends Element {
this.#renderBrowserState()
}

const hasMutations = this.mutations && this.mutations.length
const autoScreenshot = hasMutations ? null : this.#latestAutoScreenshot
const displayScreenshot = this.#screenshotData ?? autoScreenshot

return html`
<section
class="w-full h-full bg-sideBarBackground rounded-lg border-2 border-panelBorder shadow-xl"
Expand All @@ -417,11 +458,28 @@ export class DevtoolsBrowser extends Element {
<span class="truncate">${this.#activeUrl}</span>
</div>
</header>
${this.mutations && this.mutations.length
? html`<iframe class="origin-top-left"></iframe>`
: html`<wdio-devtools-placeholder
style="height: 100%"
></wdio-devtools-placeholder>`}
${this.#screenshotData
? html`
<div class="iframe-wrapper">
<div class="screenshot-overlay" style="position:relative;flex:1;min-height:0;">
<img src="data:image/png;base64,${this.#screenshotData}" />
</div>
</div>`
: hasMutations
? html`
<div class="iframe-wrapper">
<iframe class="origin-top-left"></iframe>
</div>`
: displayScreenshot
? html`
<div class="iframe-wrapper">
<div class="screenshot-overlay" style="position:relative;flex:1;min-height:0;">
<img src="data:image/png;base64,${displayScreenshot}" />
</div>
</div>`
: html`<wdio-devtools-placeholder
style="height: 100%"
></wdio-devtools-placeholder>`}
</section>
`
}
Expand Down
8 changes: 8 additions & 0 deletions packages/app/src/components/sidebar/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import { TestState } from './types.js'

export const STATE_MAP: Record<string, TestState> = {
running: TestState.RUNNING,
failed: TestState.FAILED,
passed: TestState.PASSED,
skipped: TestState.SKIPPED
}
import type { RunCapabilities } from './types.js'

export const DEFAULT_CAPABILITIES: RunCapabilities = {
Expand Down
Loading
Loading