|
| 1 | +/** |
| 2 | + * WordPress dependencies |
| 3 | + */ |
| 4 | +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); |
| 5 | + |
| 6 | +test.use( { |
| 7 | + previewUtils: async ( { page }, use ) => { |
| 8 | + await use( new PreviewUtils( { page } ) ); |
| 9 | + }, |
| 10 | +} ); |
| 11 | + |
| 12 | +test.describe( 'Preview', () => { |
| 13 | + test.beforeEach( async ( { admin } ) => { |
| 14 | + await admin.createNewPost(); |
| 15 | + } ); |
| 16 | + |
| 17 | + test( 'should open a preview window for a new post', async ( { |
| 18 | + editor, |
| 19 | + page, |
| 20 | + previewUtils, |
| 21 | + requestUtils, |
| 22 | + } ) => { |
| 23 | + const editorPage = page; |
| 24 | + |
| 25 | + await editor.canvas |
| 26 | + .locator( 'role=textbox[name="Add title"i]' ) |
| 27 | + .type( 'Hello World' ); |
| 28 | + |
| 29 | + const previewPage = await editor.openPreviewPage( editorPage ); |
| 30 | + const previewTitle = previewPage.locator( 'role=heading[level=1]' ); |
| 31 | + |
| 32 | + await expect( previewTitle ).toHaveText( 'Hello World' ); |
| 33 | + |
| 34 | + // When autosave completes for a new post, the URL of the editor should |
| 35 | + // update to include the ID. Use this to assert on preview URL. |
| 36 | + await expect( editorPage ).toHaveURL( /[\?&]post=(\d+)/ ); |
| 37 | + const [ , postId ] = editorPage.url().match( /[\?&]post=(\d+)/ ); |
| 38 | + |
| 39 | + const expectedPreviewURL = new URL( requestUtils.baseURL ); |
| 40 | + expectedPreviewURL.search = `?p=${ postId }&preview=true`; |
| 41 | + await expect( previewPage ).toHaveURL( expectedPreviewURL.href ); |
| 42 | + |
| 43 | + // Return to editor to change title. |
| 44 | + await editorPage.bringToFront(); |
| 45 | + await editor.canvas |
| 46 | + .locator( 'role=textbox[name="Add title"i]' ) |
| 47 | + .type( '!' ); |
| 48 | + await previewUtils.waitForPreviewNavigation( previewPage ); |
| 49 | + |
| 50 | + // Title in preview should match updated input. |
| 51 | + await expect( previewTitle ).toHaveText( 'Hello World!' ); |
| 52 | + |
| 53 | + // Pressing preview without changes should bring same preview window to |
| 54 | + // front and reload, but should not show interstitial. |
| 55 | + await editorPage.bringToFront(); |
| 56 | + await previewUtils.waitForPreviewNavigation( previewPage ); |
| 57 | + |
| 58 | + await expect( previewTitle ).toHaveText( 'Hello World!' ); |
| 59 | + |
| 60 | + // Preview for published post (no unsaved changes) directs to canonical URL for post. |
| 61 | + await editorPage.bringToFront(); |
| 62 | + await editor.publishPost(); |
| 63 | + |
| 64 | + // Close the panel. |
| 65 | + await page.click( 'role=button[name="Close panel"i]' ); |
| 66 | + |
| 67 | + // Return to editor to change title. |
| 68 | + await editorPage.bringToFront(); |
| 69 | + await editor.canvas |
| 70 | + .locator( 'role=textbox[name="Add title"i]' ) |
| 71 | + .fill( 'Hello World! And more.' ); |
| 72 | + await previewUtils.waitForPreviewNavigation( previewPage ); |
| 73 | + |
| 74 | + // Title in preview should match updated input. |
| 75 | + await expect( previewTitle ).toHaveText( 'Hello World! And more.' ); |
| 76 | + |
| 77 | + // Published preview URL should include ID and nonce parameters. |
| 78 | + const { searchParams } = new URL( previewPage.url() ); |
| 79 | + expect( searchParams.has( 'preview_id' ) ).toEqual( true ); |
| 80 | + expect( searchParams.has( 'preview_nonce' ) ).toEqual( true ); |
| 81 | + |
| 82 | + // Return to editor. Previewing already-autosaved preview tab should |
| 83 | + // reuse the opened tab, skipping interstitial. This resolves an edge |
| 84 | + // cases where the post is dirty but not autosaveable (because the |
| 85 | + // autosave is already up-to-date). |
| 86 | + // |
| 87 | + // See: https://github.com/WordPress/gutenberg/issues/7561 |
| 88 | + await editorPage.bringToFront(); |
| 89 | + await previewUtils.waitForPreviewNavigation( previewPage ); |
| 90 | + |
| 91 | + // Title in preview should match updated input. |
| 92 | + await expect( previewTitle ).toHaveText( 'Hello World! And more.' ); |
| 93 | + |
| 94 | + await previewPage.close(); |
| 95 | + } ); |
| 96 | + |
| 97 | + test( 'should not revert title during a preview right after a save draft', async ( { |
| 98 | + editor, |
| 99 | + page, |
| 100 | + previewUtils, |
| 101 | + } ) => { |
| 102 | + await editor.openDocumentSettingsSidebar(); |
| 103 | + |
| 104 | + const editorPage = page; |
| 105 | + |
| 106 | + // Type aaaaa in the title field. |
| 107 | + await editor.canvas |
| 108 | + .locator( 'role=textbox[name="Add title"]' ) |
| 109 | + .type( 'aaaaa' ); |
| 110 | + await editorPage.keyboard.press( 'Tab' ); |
| 111 | + |
| 112 | + // Save the post as a draft. |
| 113 | + await editorPage.click( 'role=button[name="Save draft"i]' ); |
| 114 | + await editorPage.waitForSelector( |
| 115 | + 'role=button[name="Dismiss this notice"] >> text=Draft saved' |
| 116 | + ); |
| 117 | + |
| 118 | + // Open the preview page. |
| 119 | + const previewPage = await editor.openPreviewPage( editorPage ); |
| 120 | + |
| 121 | + // Title in preview should match input. |
| 122 | + const previewTitle = previewPage.locator( 'role=heading[level=1]' ); |
| 123 | + await expect( previewTitle ).toHaveText( 'aaaaa' ); |
| 124 | + |
| 125 | + // Return to editor. |
| 126 | + await editorPage.bringToFront(); |
| 127 | + |
| 128 | + // Append bbbbb to the title, and tab away from the title so blur event is triggered. |
| 129 | + await editor.canvas |
| 130 | + .locator( 'role=textbox[name="Add title"i]' ) |
| 131 | + .fill( 'aaaaabbbbb' ); |
| 132 | + await editorPage.keyboard.press( 'Tab' ); |
| 133 | + |
| 134 | + // Save draft and open the preview page right after. |
| 135 | + await editorPage.click( 'role=button[name="Save draft"i]' ); |
| 136 | + await editorPage.waitForSelector( |
| 137 | + 'role=button[name="Dismiss this notice"] >> text=Draft saved' |
| 138 | + ); |
| 139 | + await previewUtils.waitForPreviewNavigation( previewPage ); |
| 140 | + |
| 141 | + // Title in preview should match updated input. |
| 142 | + await expect( previewTitle ).toHaveText( 'aaaaabbbbb' ); |
| 143 | + |
| 144 | + await previewPage.close(); |
| 145 | + } ); |
| 146 | + |
| 147 | + // Verify correct preview. See: https://github.com/WordPress/gutenberg/issues/33616 |
| 148 | + test( 'should display the correct preview when switching between published and draft statuses', async ( { |
| 149 | + editor, |
| 150 | + page, |
| 151 | + previewUtils, |
| 152 | + } ) => { |
| 153 | + const editorPage = page; |
| 154 | + |
| 155 | + // Type Lorem in the title field. |
| 156 | + await editor.canvas |
| 157 | + .locator( 'role=textbox[name="Add title"i]' ) |
| 158 | + .type( 'Lorem' ); |
| 159 | + await editor.openDocumentSettingsSidebar(); |
| 160 | + |
| 161 | + // Open the preview page. |
| 162 | + const previewPage = await editor.openPreviewPage( editorPage ); |
| 163 | + |
| 164 | + // Title in preview should match input. |
| 165 | + const previewTitle = previewPage.locator( 'role=heading[level=1]' ); |
| 166 | + await expect( previewTitle ).toHaveText( 'Lorem' ); |
| 167 | + |
| 168 | + // Return to editor and publish post. |
| 169 | + await editorPage.bringToFront(); |
| 170 | + await editor.publishPost(); |
| 171 | + |
| 172 | + // Close the panel. |
| 173 | + await page.click( 'role=button[name="Close panel"i]' ); |
| 174 | + |
| 175 | + // Change the title and preview again. |
| 176 | + await editor.canvas |
| 177 | + .locator( 'role=textbox[name="Add title"i]' ) |
| 178 | + .type( ' Ipsum' ); |
| 179 | + await previewUtils.waitForPreviewNavigation( previewPage ); |
| 180 | + |
| 181 | + // Title in preview should match updated input. |
| 182 | + await expect( previewTitle ).toHaveText( 'Lorem Ipsum' ); |
| 183 | + |
| 184 | + // Return to editor and switch to Draft. |
| 185 | + await editorPage.bringToFront(); |
| 186 | + await page.getByRole( 'button', { name: 'Change status:' } ).click(); |
| 187 | + await page.getByRole( 'radio', { name: 'Draft' } ).click(); |
| 188 | + await page |
| 189 | + .getByRole( 'region', { name: 'Editor top bar' } ) |
| 190 | + .getByRole( 'button', { |
| 191 | + name: 'Save', |
| 192 | + exact: true, |
| 193 | + } ) |
| 194 | + .click(); |
| 195 | + |
| 196 | + // Wait for the status change. |
| 197 | + // @see https://github.com/WordPress/gutenberg/pull/43933 |
| 198 | + await expect( |
| 199 | + page.locator( 'role=button[name="Publish"i]' ) |
| 200 | + ).toBeVisible(); |
| 201 | + |
| 202 | + // Change the title. |
| 203 | + await editor.canvas |
| 204 | + .locator( 'role=textbox[name="Add title"i]' ) |
| 205 | + .type( ' Draft' ); |
| 206 | + |
| 207 | + // Open the preview page. |
| 208 | + await previewUtils.waitForPreviewNavigation( previewPage ); |
| 209 | + |
| 210 | + // Title in preview should match updated input. |
| 211 | + await expect( previewTitle ).toHaveText( 'Lorem Ipsum Draft' ); |
| 212 | + |
| 213 | + await previewPage.close(); |
| 214 | + } ); |
| 215 | +} ); |
| 216 | + |
| 217 | +test.describe( 'Preview with Custom Fields enabled', () => { |
| 218 | + test.beforeEach( async ( { admin, previewUtils } ) => { |
| 219 | + await admin.createNewPost(); |
| 220 | + await previewUtils.toggleCustomFieldsOption( true ); |
| 221 | + } ); |
| 222 | + |
| 223 | + test.afterEach( async ( { previewUtils } ) => { |
| 224 | + await previewUtils.toggleCustomFieldsOption( false ); |
| 225 | + } ); |
| 226 | + |
| 227 | + // Catch regressions of https://github.com/WordPress/gutenberg/issues/12617 |
| 228 | + test( 'displays edits to the post title and content in the preview', async ( { |
| 229 | + editor, |
| 230 | + page, |
| 231 | + previewUtils, |
| 232 | + } ) => { |
| 233 | + const editorPage = page; |
| 234 | + |
| 235 | + // Add an initial title and content. |
| 236 | + await editor.canvas |
| 237 | + .locator( 'role=textbox[name="Add title"i]' ) |
| 238 | + .type( 'title 1' ); |
| 239 | + await editor.insertBlock( { |
| 240 | + name: 'core/paragraph', |
| 241 | + attributes: { content: 'content 1' }, |
| 242 | + } ); |
| 243 | + |
| 244 | + // Publish the post and then close the publish panel. |
| 245 | + await editor.publishPost(); |
| 246 | + |
| 247 | + // Close the panel. |
| 248 | + await page.click( 'role=button[name="Close panel"i]' ); |
| 249 | + |
| 250 | + // Open the preview page. |
| 251 | + const previewPage = await editor.openPreviewPage(); |
| 252 | + |
| 253 | + // Check the title and preview match. |
| 254 | + const previewTitle = previewPage.locator( 'role=heading[level=1]' ); |
| 255 | + await expect( previewTitle ).toHaveText( 'title 1' ); |
| 256 | + // No semantics we can grab here; it's just a <p> inside a <div> :') |
| 257 | + const previewContent = previewPage.locator( '.entry-content p' ); |
| 258 | + await expect( previewContent ).toHaveText( 'content 1' ); |
| 259 | + |
| 260 | + // Return to editor and modify the title and content. |
| 261 | + await editorPage.bringToFront(); |
| 262 | + await editor.canvas |
| 263 | + .locator( 'role=textbox[name="Add title"i]' ) |
| 264 | + .fill( 'title 2' ); |
| 265 | + await editor.canvas |
| 266 | + .locator( 'role=document >> text="content 1"' ) |
| 267 | + .fill( 'content 2' ); |
| 268 | + |
| 269 | + // Open the preview page. |
| 270 | + await previewUtils.waitForPreviewNavigation( previewPage ); |
| 271 | + |
| 272 | + // Title in preview should match input. |
| 273 | + await expect( previewTitle ).toHaveText( 'title 2' ); |
| 274 | + await expect( previewContent ).toHaveText( 'content 2' ); |
| 275 | + |
| 276 | + // Make sure the editor is active for the afterEach function. |
| 277 | + await editorPage.bringToFront(); |
| 278 | + } ); |
| 279 | +} ); |
| 280 | + |
| 281 | +test.describe( 'Preview with private custom post type', () => { |
| 282 | + test.beforeAll( async ( { requestUtils } ) => { |
| 283 | + await requestUtils.activatePlugin( 'gutenberg-test-custom-post-types' ); |
| 284 | + } ); |
| 285 | + |
| 286 | + test.afterAll( async ( { requestUtils } ) => { |
| 287 | + await requestUtils.deactivatePlugin( |
| 288 | + 'gutenberg-test-custom-post-types' |
| 289 | + ); |
| 290 | + } ); |
| 291 | + |
| 292 | + test( 'should not show the Preview Externally link', async ( { |
| 293 | + admin, |
| 294 | + page, |
| 295 | + } ) => { |
| 296 | + await admin.createNewPost( { |
| 297 | + postType: 'not_public', |
| 298 | + title: 'aaaaa', |
| 299 | + } ); |
| 300 | + |
| 301 | + // Open the view menu. |
| 302 | + await page.click( 'role=button[name="View"i]' ); |
| 303 | + |
| 304 | + await expect( |
| 305 | + page.locator( 'role=menuitem[name="Preview in new tab"i]' ) |
| 306 | + ).toBeHidden(); |
| 307 | + } ); |
| 308 | +} ); |
| 309 | + |
| 310 | +class PreviewUtils { |
| 311 | + constructor( { page } ) { |
| 312 | + this.page = page; |
| 313 | + } |
| 314 | + |
| 315 | + async waitForPreviewNavigation( previewPage ) { |
| 316 | + const previewToggle = this.page.locator( |
| 317 | + 'role=button[name="View"i][expanded=false]' |
| 318 | + ); |
| 319 | + const isDropdownClosed = await previewToggle.isVisible(); |
| 320 | + if ( isDropdownClosed ) { |
| 321 | + await previewToggle.click(); |
| 322 | + } |
| 323 | + |
| 324 | + await this.page.click( 'role=menuitem[name="Preview in new tab"i]' ); |
| 325 | + return previewPage.waitForNavigation(); |
| 326 | + } |
| 327 | + |
| 328 | + async toggleCustomFieldsOption( shouldBeChecked ) { |
| 329 | + // Open preferences dialog. |
| 330 | + |
| 331 | + await this.page.click( |
| 332 | + 'role=region[name="Editor top bar"i] >> role=button[name="Options"i]' |
| 333 | + ); |
| 334 | + await this.page.click( 'role=menuitem[name="Preferences"i]' ); |
| 335 | + |
| 336 | + // Navigate to general section. |
| 337 | + await this.page.click( |
| 338 | + 'role=dialog[name="Preferences"i] >> role=tab[name="General"i]' |
| 339 | + ); |
| 340 | + |
| 341 | + // Find custom fields checkbox. |
| 342 | + const customFieldsCheckbox = this.page.locator( |
| 343 | + 'role=checkbox[name="Custom fields"i]' |
| 344 | + ); |
| 345 | + |
| 346 | + if ( shouldBeChecked ) { |
| 347 | + await customFieldsCheckbox.check(); |
| 348 | + } else { |
| 349 | + await customFieldsCheckbox.uncheck(); |
| 350 | + } |
| 351 | + |
| 352 | + const saveButton = this.page.locator( |
| 353 | + 'role=button[name=/(Dis|En)able & Reload/i]' |
| 354 | + ); |
| 355 | + const isSaveVisible = await saveButton.isVisible(); |
| 356 | + |
| 357 | + if ( isSaveVisible ) { |
| 358 | + saveButton.click(); |
| 359 | + await this.page.waitForNavigation(); |
| 360 | + return; |
| 361 | + } |
| 362 | + |
| 363 | + await this.page.click( |
| 364 | + 'role=dialog[name="Preferences"i] >> role=button[name="Close"i]' |
| 365 | + ); |
| 366 | + } |
| 367 | +} |
0 commit comments