1- import type { FrameLocator } from '@playwright/test'
1+ import type { FrameLocator , Page } from '@playwright/test'
22import { test as base , expect } from '@playwright/test'
33
4- // Vite DevTools renders the Nuxt DevTools dock entry as an iframe nested inside its
5- // own shadow DOM. The iframe has no stable id, so we target by src.
4+ // The Nuxt DevTools client renders inside an iframe that Vite DevTools mounts in
5+ // its own shadow DOM. The iframe has no stable id, so we target it by src.
66const IFRAME_SELECTOR = 'iframe[src*="__nuxt_devtools__/client"]'
77
88interface DevToolsFixtures {
99 playground : string
1010 mode : 'dev' | 'built'
11+ /** Open the DevTools panel and wait for the client app to hydrate. */
1112 openDevTools : ( ) => Promise < void >
13+ /** Navigate the open DevTools client to a tab route (e.g. `/modules/modules`). */
1214 navigateTab : ( path : string ) => Promise < void >
15+ /** Locator scoped to the DevTools client iframe. */
1316 devtoolsFrame : ( ) => FrameLocator
1417}
1518
19+ // e2e servers run with `VITE_DEVTOOLS_DISABLE_CLIENT_AUTH=true`, which trusts the
20+ // *server* peer (so RPC is allowed) but never flips the *client-side* trust flag.
21+ // Until it does, Vite DevTools never subscribes to the dock list, so no dock —
22+ // and therefore no Nuxt group — ever appears. Nudge the flag here. This is purely
23+ // test-environment plumbing; it is not something the tests assert on.
24+ async function ensureDockReady ( page : Page ) : Promise < void > {
25+ await page . waitForFunction (
26+ ( ) => Boolean ( ( globalThis as any ) . __NUXT_DEVTOOLS_HOST__ ?. devtools ) ,
27+ null ,
28+ { timeout : 30_000 } ,
29+ )
30+ await page . waitForFunction (
31+ ( ) => Boolean ( ( globalThis as any ) . __VITE_DEVTOOLS_CLIENT_CONTEXT__ ?. rpc ) ,
32+ null ,
33+ { timeout : 30_000 } ,
34+ )
35+ await page . evaluate ( ( ) => {
36+ const ctx = ( globalThis as any ) . __VITE_DEVTOOLS_CLIENT_CONTEXT__
37+ if ( ctx ?. rpc && ! ctx . rpc . isTrusted )
38+ ctx . rpc . events ?. emit ?.( 'rpc:is-trusted:updated' , true )
39+ } )
40+ await page . waitForFunction (
41+ ( ) => Boolean ( ( globalThis as any ) . __VITE_DEVTOOLS_CLIENT_CONTEXT__ ?. docks ?. entries ?. length ) ,
42+ null ,
43+ { timeout : 30_000 } ,
44+ )
45+ }
46+
1647export const test = base . extend < DevToolsFixtures > ( {
1748 // eslint-disable-next-line no-empty-pattern
1849 playground : async ( { } , use , info ) => {
@@ -25,58 +56,23 @@ export const test = base.extend<DevToolsFixtures>({
2556
2657 openDevTools : async ( { page } , use ) => {
2758 await use ( async ( ) => {
28- // Wait for the Vite DevTools client context to be injected.
29- await page . waitForFunction (
30- ( ) => Boolean ( ( globalThis as any ) . __VITE_DEVTOOLS_CLIENT_CONTEXT__ ?. rpc ) ,
31- null ,
32- { timeout : 30_000 } ,
33- )
34- // Devframe 0.7 only fetches the `devframe:docks` shared state once the
35- // client is marked trusted (via the `rpc:is-trusted:updated` event).
36- // `VITE_DEVTOOLS_DISABLE_CLIENT_AUTH` trusts the *server* peer — so RPC
37- // calls are allowed — but never flips the *client-side* trust flag, so the
38- // dock list would otherwise stay empty forever. Nudge it here so the docks
39- // subscription kicks off. (Built/preview mode uses the static RPC backend,
40- // which is already trusted, so this is a harmless no-op there.)
41- await page . evaluate ( ( ) => {
42- const ctx = ( globalThis as any ) . __VITE_DEVTOOLS_CLIENT_CONTEXT__
43- if ( ctx ?. rpc && ! ctx . rpc . isTrusted )
44- ctx . rpc . events ?. emit ?.( 'rpc:is-trusted:updated' , true )
45- } )
46- await page . waitForFunction (
47- ( ) => Boolean ( ( globalThis as any ) . __VITE_DEVTOOLS_CLIENT_CONTEXT__ ?. docks ?. entries ?. length ) ,
48- null ,
49- { timeout : 30_000 } ,
50- )
51- // Drive Vite DevTools directly: open the panel, then switch the active
52- // dock entry to nuxt:devtools (idempotent; safe to call when already open).
53- await page . evaluate ( async ( ) => {
54- const ctx = ( globalThis as any ) . __VITE_DEVTOOLS_CLIENT_CONTEXT__
55- ctx . panel . store . open = true
56- await ctx . docks . switchEntry ( 'nuxt:devtools' )
57- } )
58- // Iframe creation is async after switchEntry resolves. Poll the dock entry
59- // state until its DOM iframe is attached.
60- await page . waitForFunction ( ( ) => {
61- const ctx = ( globalThis as any ) . __VITE_DEVTOOLS_CLIENT_CONTEXT__
62- const state = ctx ?. docks ?. getStateById ?.( 'nuxt:devtools' )
63- return Boolean ( state ?. domElements ?. iframe ?. isConnected )
64- } , null , { timeout : 30_000 } )
65- // Then wait for the inner app to hydrate. Generous timeout: playgrounds that
66- // use `../../local` spawn a separate Nuxt dev subprocess for the devtools
67- // client, and its first Vite compile is slow on cold start.
68- await page . frameLocator ( IFRAME_SELECTOR ) . locator ( '#nuxt-devtools-app' ) . waitFor ( { state : 'attached' , timeout : 90_000 } )
59+ await ensureDockReady ( page )
60+ // Drive the *public* host API the same way the keyboard shortcut / host app
61+ // would — no reaching into Vite DevTools' internal panel/dock state.
62+ await page . evaluate ( ( ) => ( globalThis as any ) . __NUXT_DEVTOOLS_HOST__ . devtools . open ( ) )
63+ // Wait for the client app inside the iframe to hydrate.
64+ await page . frameLocator ( IFRAME_SELECTOR )
65+ . locator ( '#nuxt-devtools-app' )
66+ . waitFor ( { state : 'attached' , timeout : 60_000 } )
6967 } )
7068 } ,
7169
7270 navigateTab : async ( { page } , use ) => {
7371 await use ( async ( path : string ) => {
74- // Drive navigation through the documented host hook.
75- // (`__NUXT_DEVTOOLS_HOST__.devtools.navigate` itself currently writes to
76- // `ctx.panel.store.value.open`, which assumes a Vue ref but the kit exposes
77- // a plain object — using the hook avoids that landmine.)
72+ // Navigate via the documented host API (`devtools.navigate`) rather than
73+ // poking the frame-nav hook or router directly.
7874 await page . evaluate (
79- p => ( window as any ) . __NUXT_DEVTOOLS_HOST__ . hooks . callHook ( 'host:action: navigate' , p ) ,
75+ p => ( globalThis as any ) . __NUXT_DEVTOOLS_HOST__ . devtools . navigate ( p ) ,
8076 path ,
8177 )
8278 } )
0 commit comments