@@ -178,6 +178,119 @@ try {
178178 ? await ( engine === 'webkit' ? webkit : chromium ) . launch ( )
179179 : await webkit . connect ( webkitEndpoint , { timeout : 180_000 } )
180180 const feedbackBrowser = browser
181+ await check (
182+ 'Section permalinks copy the full URL and preserve native link navigation' ,
183+ async ( ) => {
184+ const context = await feedbackBrowser . newContext ( {
185+ permissions : [ 'clipboard-read' , 'clipboard-write' ] ,
186+ viewport : { width : 1440 , height : 900 } ,
187+ } )
188+ try {
189+ const page = await context . newPage ( )
190+ await open ( page , '/files/editor?view=reference' )
191+ const formatting = page . locator ( '#formatting-text' ) . getByRole ( 'link' )
192+ await formatting . click ( )
193+ const first = new URL ( '/files/editor?view=reference#formatting-text' , target ) . href
194+ await expect ( page ) . toHaveURL ( first )
195+ await expect ( formatting ) . toHaveAccessibleName ( 'Link copied' )
196+ await expect . poll ( ( ) => page . evaluate ( ( ) => navigator . clipboard . readText ( ) ) ) . toBe ( first )
197+
198+ const tables = page . locator ( '#tables' ) . getByRole ( 'link' )
199+ await tables . focus ( )
200+ await page . keyboard . press ( 'Enter' )
201+ const second = new URL ( '/files/editor?view=reference#tables' , target ) . href
202+ await expect ( page ) . toHaveURL ( second )
203+ await expect . poll ( ( ) => page . evaluate ( ( ) => navigator . clipboard . readText ( ) ) ) . toBe ( second )
204+ const [ newTab ] = await Promise . all ( [
205+ context . waitForEvent ( 'page' , { timeout : 10_000 } ) ,
206+ page
207+ . locator ( '#code-blocks' )
208+ . getByRole ( 'link' )
209+ . click ( { modifiers : [ 'ControlOrMeta' ] } ) ,
210+ ] )
211+ await newTab . waitForLoadState ( 'domcontentloaded' )
212+ await expect ( newTab ) . toHaveURL (
213+ new URL ( '/files/editor?view=reference#code-blocks' , target ) . href
214+ )
215+ await newTab . close ( )
216+ await page . bringToFront ( )
217+ await expect ( page ) . toHaveURL ( second )
218+ expect ( await page . evaluate ( ( ) => navigator . clipboard . readText ( ) ) ) . toBe ( second )
219+ await page . screenshot ( { path : join ( directory , 'section-permalink.png' ) } )
220+ } finally {
221+ await context . close ( )
222+ }
223+ } ,
224+ engine === 'webkit'
225+ ? 'Playwright WebKit cannot grant clipboard read/write permissions; Chromium verifies native clipboard contents.'
226+ : undefined
227+ )
228+ await check ( 'Section permalink navigation survives an unavailable clipboard' , async ( ) => {
229+ const context = await feedbackBrowser . newContext ( )
230+ try {
231+ await context . addInitScript ( ( ) => {
232+ Object . defineProperty ( navigator , 'clipboard' , { value : undefined } )
233+ } )
234+ const page = await context . newPage ( )
235+ await open ( page , '/files/editor' )
236+ const link = page . locator ( '#formatting-text' ) . getByRole ( 'link' )
237+ await link . click ( )
238+ await expect ( page ) . toHaveURL ( new URL ( '/files/editor#formatting-text' , target ) . href )
239+ await expect ( link ) . toHaveAccessibleName ( 'Could not copy. Copy the link from the address bar.' )
240+ await expect ( page . getByRole ( 'status' ) . filter ( { hasText : 'Could not copy.' } ) ) . toBeVisible ( )
241+ await page . screenshot ( { path : join ( directory , 'section-permalink-copy-unavailable.png' ) } )
242+ await page
243+ . getByRole ( 'navigation' , { name : 'Page navigation' , exact : true } )
244+ . getByRole ( 'link' , { name : 'Previous Files' , exact : true } )
245+ . click ( )
246+ await expect ( page ) . toHaveURL ( new URL ( '/files' , target ) . href )
247+ await expect (
248+ page . getByRole ( 'link' , { name : 'Could not copy. Copy the link from the address bar.' } )
249+ ) . toHaveCount ( 0 )
250+ } finally {
251+ await context . close ( )
252+ }
253+ } )
254+ await check (
255+ 'Compact page outline dismisses with Escape and preserves new-tab clicks' ,
256+ async ( ) => {
257+ for ( const width of [ 390 , 1100 ] ) {
258+ const context = await feedbackBrowser . newContext ( { viewport : { width, height : 900 } } )
259+ try {
260+ const page = await context . newPage ( )
261+ await open ( page , '/files/editor' )
262+ const summary = page
263+ . locator ( 'summary' )
264+ . filter ( { hasText : 'On this page' } )
265+ . filter ( { visible : true } )
266+ const details = summary . locator ( '..' )
267+ await summary . click ( )
268+ await expect ( details ) . toHaveAttribute ( 'open' , '' )
269+ await details . getByRole ( 'link' , { name : 'Formatting text' , exact : true } ) . focus ( )
270+ await page . keyboard . press ( 'Escape' )
271+ await expect ( details ) . not . toHaveAttribute ( 'open' )
272+ await expect ( summary ) . toBeFocused ( )
273+
274+ await summary . click ( )
275+ const link = details . getByRole ( 'link' , { name : 'Formatting text' , exact : true } )
276+ const [ newTab ] = await Promise . all ( [
277+ context . waitForEvent ( 'page' , { timeout : 10_000 } ) ,
278+ link . click ( { modifiers : [ 'ControlOrMeta' ] } ) ,
279+ ] )
280+ await newTab . waitForLoadState ( 'domcontentloaded' )
281+ await expect ( newTab ) . toHaveURL ( new URL ( '/files/editor#formatting-text' , target ) . href )
282+ await expect ( details ) . toHaveAttribute ( 'open' , '' )
283+ await newTab . close ( )
284+ await link . click ( )
285+ await expect ( page ) . toHaveURL ( new URL ( '/files/editor#formatting-text' , target ) . href )
286+ await expect ( details ) . not . toHaveAttribute ( 'open' )
287+ await page . screenshot ( { path : join ( directory , `section-outline-${ width } .png` ) } )
288+ } finally {
289+ await context . close ( )
290+ }
291+ }
292+ }
293+ )
181294 await check ( 'Feedback forwards votes and preserves unchanged retry identity' , async ( ) => {
182295 const cert = process . env . DOCS_FEEDBACK_TEST_CERT
183296 const key = process . env . DOCS_FEEDBACK_TEST_KEY
0 commit comments