11import { expect , test } from '@playwright/test' ;
22import { globalDataRequests , mockApi , responseFixtures } from '@tryghost/admin-x-framework/test/acceptance' ;
3+ import type { Page } from '@playwright/test' ;
34
45const automatedEmailsFixture = {
56 automated_emails : [ {
@@ -22,6 +23,32 @@ const newslettersRequest = {
2223 browseNewslettersLimit : { method : 'GET' , path : '/newsletters/?filter=status%3Aactive&limit=1' , response : responseFixtures . newsletters }
2324} ;
2425
26+ const configWithWelcomeEmailEditorEnabled = {
27+ ...responseFixtures . config ,
28+ config : {
29+ ...responseFixtures . config . config ,
30+ labs : {
31+ ...responseFixtures . config . config . labs ,
32+ welcomeEmailEditor : true
33+ }
34+ }
35+ } ;
36+
37+ const pasteText = async ( page : Page , content : string ) => {
38+ await page . evaluate ( ( text : string ) => {
39+ const dataTransfer = new DataTransfer ( ) ;
40+ dataTransfer . setData ( 'text/plain' , text ) ;
41+
42+ document . activeElement ?. dispatchEvent ( new ClipboardEvent ( 'paste' , {
43+ clipboardData : dataTransfer ,
44+ bubbles : true ,
45+ cancelable : true
46+ } ) ) ;
47+
48+ dataTransfer . clearData ( ) ;
49+ } , content ) ;
50+ } ;
51+
2552test . describe ( 'Member emails settings' , async ( ) => {
2653 test . describe ( 'Welcome email modal' , async ( ) => {
2754 test ( 'Escape key closes test email dropdown without closing modal' , async ( { page} ) => {
@@ -196,6 +223,94 @@ test.describe('Member emails settings', async () => {
196223 } ) ;
197224 } ) ;
198225
226+ test ( 'welcome email editor pastes URL and fetches embed metadata' , async ( { page} ) => {
227+ const { lastApiRequests} = await mockApi ( { page, requests : {
228+ ...globalDataRequests ,
229+ ...newslettersRequest ,
230+ browseConfig : { method : 'GET' , path : '/config/' , response : configWithWelcomeEmailEditorEnabled } ,
231+ browseAutomatedEmails : { method : 'GET' , path : '/automated_emails/' , response : automatedEmailsFixture } ,
232+ fetchOembed : {
233+ method : 'GET' ,
234+ path : / ^ \/ o e m b e d \/ \? / ,
235+ response : {
236+ type : 'video' ,
237+ html : '<iframe width="200" height="113" src="https://www.youtube.com/embed/8YWl7tDGUPA?feature=oembed" frameborder="0" allowfullscreen></iframe>'
238+ }
239+ }
240+ } } ) ;
241+
242+ await page . goto ( '/#/memberemails' ) ;
243+ await page . waitForLoadState ( 'networkidle' ) ;
244+
245+ const section = page . getByTestId ( 'memberemails' ) ;
246+ await expect ( section ) . toBeVisible ( { timeout : 10000 } ) ;
247+ await section . getByTestId ( 'free-welcome-email-preview' ) . click ( ) ;
248+
249+ const modal = page . getByTestId ( 'welcome-email-modal' ) ;
250+ await expect ( modal ) . toBeVisible ( ) ;
251+
252+ const editor = modal . locator ( '[data-kg="editor"] div[contenteditable="true"]' ) . first ( ) ;
253+ await editor . click ( { timeout : 5000 } ) ;
254+ await page . keyboard . press ( 'ControlOrMeta+a' ) ;
255+ await page . keyboard . press ( 'Backspace' ) ;
256+
257+ await pasteText ( page , 'https://ghost.org/' ) ;
258+
259+ await expect ( modal . getByTestId ( 'embed-iframe' ) ) . toBeVisible ( ) ;
260+
261+ await expect . poll ( ( ) => lastApiRequests . fetchOembed ?. url || '' ) . toContain ( '/oembed/?' ) ;
262+ await expect . poll ( ( ) => lastApiRequests . fetchOembed ?. url || '' ) . toContain ( 'url=https%3A%2F%2Fghost.org%2F' ) ;
263+ } ) ;
264+
265+ test ( 'welcome email editor bookmark card fetches bookmark metadata' , async ( { page} ) => {
266+ const { lastApiRequests} = await mockApi ( { page, requests : {
267+ ...globalDataRequests ,
268+ ...newslettersRequest ,
269+ browseConfig : { method : 'GET' , path : '/config/' , response : configWithWelcomeEmailEditorEnabled } ,
270+ browseAutomatedEmails : { method : 'GET' , path : '/automated_emails/' , response : automatedEmailsFixture } ,
271+ fetchOembed : {
272+ method : 'GET' ,
273+ path : / ^ \/ o e m b e d \/ \? / ,
274+ response : {
275+ url : 'https://ghost.org/' ,
276+ metadata : {
277+ icon : 'https://ghost.org/favicon.ico' ,
278+ title : 'Ghost: The Creator Economy Platform' ,
279+ description : 'Build independent publishing businesses and memberships.' ,
280+ publisher : 'Ghost.org' ,
281+ author : 'Ghost' ,
282+ thumbnail : 'https://ghost.org/images/meta/ghost.png'
283+ }
284+ }
285+ }
286+ } } ) ;
287+
288+ await page . goto ( '/#/memberemails' ) ;
289+ await page . waitForLoadState ( 'networkidle' ) ;
290+
291+ const section = page . getByTestId ( 'memberemails' ) ;
292+ await expect ( section ) . toBeVisible ( { timeout : 10000 } ) ;
293+ await section . getByTestId ( 'free-welcome-email-preview' ) . click ( ) ;
294+
295+ const modal = page . getByTestId ( 'welcome-email-modal' ) ;
296+ await expect ( modal ) . toBeVisible ( ) ;
297+
298+ const editor = modal . locator ( '[data-kg="editor"] div[contenteditable="true"]' ) . first ( ) ;
299+ await editor . click ( { timeout : 5000 } ) ;
300+ await page . keyboard . press ( 'ControlOrMeta+a' ) ;
301+ await page . keyboard . press ( 'Backspace' ) ;
302+ await page . keyboard . type ( '/bookmark' ) ;
303+ await page . keyboard . press ( 'Enter' ) ;
304+
305+ const bookmarkUrlInput = modal . getByTestId ( 'bookmark-url' ) ;
306+ await expect ( bookmarkUrlInput ) . toBeVisible ( ) ;
307+ await bookmarkUrlInput . fill ( 'https://ghost.org/' ) ;
308+ await bookmarkUrlInput . press ( 'Enter' ) ;
309+
310+ await expect ( modal . getByTestId ( 'bookmark-title' ) ) . toContainText ( 'Ghost: The Creator Economy Platform' ) ;
311+ await expect . poll ( ( ) => lastApiRequests . fetchOembed ?. url || '' ) . toContain ( 'type=bookmark' ) ;
312+ } ) ;
313+
199314 test ( 'uses automated email sender fields when populated, even if newsletter differs' , async ( { page} ) => {
200315 const populatedAutomatedEmailsFixture = {
201316 automated_emails : [ {
0 commit comments