@@ -54,10 +54,27 @@ const runtimeDeckHtml = `<!doctype html>
5454const runtimePlainHtml = `<!doctype html>
5555<html><head><script>
5656 const style = document.createElement("style");
57- style.textContent = ".runtime { color: rgb(1, 2, 3); }";
57+ style.textContent =
58+ ".runtime { color: rgb(1, 2, 3); }" +
59+ ".reveal { opacity: 0; }" +
60+ ".reveal.visible { opacity: 1; }" +
61+ ".entry { animation: fade-in 700ms linear forwards; }" +
62+ "@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }";
5863 document.head.appendChild(style);
64+ addEventListener("DOMContentLoaded", () => {
65+ const target = document.querySelector(".reveal");
66+ if (target) new IntersectionObserver(() => target.classList.add("visible"), { threshold: 0 }).observe(target);
67+ });
5968</script></head>
60- <body><p class="runtime">Runtime source-tab content</p></body></html>` ;
69+ <body>
70+ <p class="runtime reveal">Observer reveal</p>
71+ <p class="runtime entry">Animated entry</p>
72+ <p class="runtime">Runtime source-tab content</p>
73+ </body></html>` ;
74+
75+ const delayedHtml = `<!doctype html><html><head>
76+ <script src="https://delayed.example/slow.js"></script>
77+ </head><body><p class="runtime">Delayed clipboard content</p></body></html>` ;
6178
6279async function seedStore ( page : Page , opts : SeedOptions ) {
6380 const now = 1_700_000_000_000 ;
@@ -107,17 +124,18 @@ async function captureClipboardHtml(page: Page) {
107124 Object . defineProperty ( window , "ClipboardItem" , {
108125 configurable : true ,
109126 value : class ClipboardItem {
110- readonly items : Record < string , Blob > ;
111- constructor ( items : Record < string , Blob > ) {
127+ readonly items : Record < string , Blob | Promise < Blob > > ;
128+ constructor ( items : Record < string , Blob | Promise < Blob > > ) {
112129 this . items = items ;
113130 }
114131 } ,
115132 } ) ;
116133 Object . defineProperty ( navigator , "clipboard" , {
117134 configurable : true ,
118135 value : {
119- write : async ( items : Array < { items : Record < string , Blob > } > ) => {
120- const blob = items [ 0 ] ?. items [ "text/html" ] ;
136+ write : async ( items : Array < { items : Record < string , Blob | Promise < Blob > > } > ) => {
137+ ( window as typeof window & { __clipboardWriteStartedAt ?: number } ) . __clipboardWriteStartedAt = performance . now ( ) ;
138+ const blob = await items [ 0 ] ?. items [ "text/html" ] ;
121139 ( window as typeof window & { __wechatClipboardHtml ?: string } ) . __wechatClipboardHtml =
122140 blob ? await blob . text ( ) : "" ;
123141 } ,
@@ -164,6 +182,9 @@ test.describe("Export menu", () => {
164182 ( window as typeof window & { __wechatClipboardHtml ?: string } ) . __wechatClipboardHtml ?? "" ,
165183 ) ;
166184 expect ( copied ) . toContain ( "color: rgb(1, 2, 3)" ) ;
185+ expect ( copied ) . toContain ( "Observer reveal" ) ;
186+ expect ( copied ) . toContain ( "Animated entry" ) ;
187+ expect ( copied ) . not . toContain ( "opacity: 0" ) ;
167188 }
168189 } ) ;
169190
@@ -186,6 +207,29 @@ test.describe("Export menu", () => {
186207 expect ( copied ) . toContain ( "color: rgb(1, 2, 3)" ) ;
187208 } ) ;
188209
210+ test ( "starts the clipboard write before a slow full-document render settles" , async ( { page } ) => {
211+ await page . route ( "https://delayed.example/slow.js" , async ( route ) => {
212+ await new Promise ( ( resolve ) => setTimeout ( resolve , 6000 ) ) ;
213+ await route . fulfill ( { contentType : "application/javascript" , body : "document.body.dataset.ready = '1';" } ) ;
214+ } ) ;
215+ await seedStore ( page , { html : delayedHtml } ) ;
216+ await page . goto ( "/" ) ;
217+ await captureClipboardHtml ( page ) ;
218+
219+ const clickStartedAt = await page . evaluate ( ( ) => performance . now ( ) ) ;
220+ await page . getByRole ( "button" , { name : / e x p o r t / i } ) . click ( ) ;
221+ await page . getByTestId ( "export-menu" ) . getByRole ( "button" , { name : / W e C h a t / } ) . click ( ) ;
222+
223+ await expect . poll ( ( ) =>
224+ page . evaluate ( ( ) => ( window as typeof window & { __wechatClipboardHtml ?: string } ) . __wechatClipboardHtml ?? "" ) ,
225+ { timeout : 12_000 } ,
226+ ) . toContain ( "Delayed clipboard content" ) ;
227+ const writeStartedAt = await page . evaluate ( ( ) =>
228+ ( window as typeof window & { __clipboardWriteStartedAt ?: number } ) . __clipboardWriteStartedAt ?? Infinity ,
229+ ) ;
230+ expect ( writeStartedAt - clickStartedAt ) . toBeLessThan ( 1000 ) ;
231+ } ) ;
232+
189233 test ( "exports a Hyperframes Remotion project zip from the UI" , async ( { page } ) => {
190234 await seedStore ( page , { html : hyperframesHtml } ) ;
191235
0 commit comments