diff --git a/ghost/admin-x-settings/src/components/settings/site/NavigationModal.tsx b/ghost/admin-x-settings/src/components/settings/site/NavigationModal.tsx index d4adc165a2..b0aa47426c 100644 --- a/ghost/admin-x-settings/src/components/settings/site/NavigationModal.tsx +++ b/ghost/admin-x-settings/src/components/settings/site/NavigationModal.tsx @@ -44,6 +44,7 @@ const NavigationModal = NiceModal.create(() => { scrolling={true} size='lg' stickyFooter={true} + testId='navigation-modal' title='Navigation' onOk={async () => { if (navigation.validate() && secondaryNavigation.validate()) { diff --git a/ghost/admin-x-settings/src/components/settings/site/navigation/NavigationEditForm.tsx b/ghost/admin-x-settings/src/components/settings/site/navigation/NavigationEditForm.tsx index 860c9b8c57..8dac082501 100644 --- a/ghost/admin-x-settings/src/components/settings/site/navigation/NavigationEditForm.tsx +++ b/ghost/admin-x-settings/src/components/settings/site/navigation/NavigationEditForm.tsx @@ -108,10 +108,11 @@ const NavigationEditForm: React.FC<{ } + action={ @@ -33,8 +33,10 @@ const NavigationItemEditor = forwardRef updateItem?.({label: e.target.value})} onKeyDown={() => clearError?.('label')} /> @@ -47,8 +49,10 @@ const NavigationItemEditor = forwardRef updateItem?.({url: value})} onKeyDown={() => clearError?.('url')} /> diff --git a/ghost/admin-x-settings/test/e2e/email/defaultRecipients.test.ts b/ghost/admin-x-settings/test/e2e/email/defaultRecipients.test.ts index 04cd2c76d1..eea67ae8d7 100644 --- a/ghost/admin-x-settings/test/e2e/email/defaultRecipients.test.ts +++ b/ghost/admin-x-settings/test/e2e/email/defaultRecipients.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Default recipient settings', async () => { test('Supports editing default recipients', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'editor_default_email_recipients', value: 'filter'}, @@ -22,7 +22,7 @@ test.describe('Default recipient settings', async () => { await section.getByLabel('Default newsletter recipients').selectOption('All members'); await section.getByRole('button', {name: 'Save'}).click(); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'editor_default_email_recipients', value: 'filter'}, {key: 'editor_default_email_recipients_filter', value: 'status:free,status:-free'} @@ -33,7 +33,7 @@ test.describe('Default recipient settings', async () => { await section.getByLabel('Default newsletter recipients').selectOption('Usually nobody'); await section.getByRole('button', {name: 'Save'}).click(); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'editor_default_email_recipients', value: 'filter'}, {key: 'editor_default_email_recipients_filter', value: null} @@ -48,7 +48,7 @@ test.describe('Default recipient settings', async () => { await expect(section.getByText('Paid-members only')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'editor_default_email_recipients', value: 'filter'}, {key: 'editor_default_email_recipients_filter', value: 'status:-free'} diff --git a/ghost/admin-x-settings/test/e2e/email/mailgun.test.ts b/ghost/admin-x-settings/test/e2e/email/mailgun.test.ts index 925ab1232d..b74b43cb8f 100644 --- a/ghost/admin-x-settings/test/e2e/email/mailgun.test.ts +++ b/ghost/admin-x-settings/test/e2e/email/mailgun.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Mailgun settings', async () => { test('Supports setting up mailgun', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'mailgun_domain', value: 'test.com'}, @@ -29,7 +29,7 @@ test.describe('Mailgun settings', async () => { await expect(section.getByText('Mailgun is set up')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'mailgun_domain', value: 'test.com'}, {key: 'mailgun_api_key', value: 'test'} diff --git a/ghost/admin-x-settings/test/e2e/general/facebook.test.ts b/ghost/admin-x-settings/test/e2e/general/facebook.test.ts index eb5b166236..9a32433dff 100644 --- a/ghost/admin-x-settings/test/e2e/general/facebook.test.ts +++ b/ghost/admin-x-settings/test/e2e/general/facebook.test.ts @@ -3,7 +3,7 @@ import {mockApi} from '../../utils/e2e'; test.describe('Facebook settings', async () => { test('Supports editing the facebook card', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { images: { upload: {images: [{url: 'http://example.com/image.png', ref: null}]} } @@ -31,7 +31,7 @@ test.describe('Facebook settings', async () => { await expect(section.getByLabel('Facebook title')).toHaveCount(0); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'og_image', value: 'http://example.com/image.png'}, {key: 'og_title', value: 'Facetitle'}, diff --git a/ghost/admin-x-settings/test/e2e/general/lockSite.test.ts b/ghost/admin-x-settings/test/e2e/general/lockSite.test.ts index 24ed5303f0..96019611e0 100644 --- a/ghost/admin-x-settings/test/e2e/general/lockSite.test.ts +++ b/ghost/admin-x-settings/test/e2e/general/lockSite.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Site password settings', async () => { test('Supports locking and unlocking the site', async ({page}) => { - let lastApiRequest = await mockApi({page, responses: { + let lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'is_private', value: true}, @@ -31,7 +31,7 @@ test.describe('Site password settings', async () => { await expect(section.getByText('Your site is password protected')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'is_private', value: true}, {key: 'password', value: 'password'} @@ -40,7 +40,7 @@ test.describe('Site password settings', async () => { // Remove the site password - lastApiRequest = await mockApi({page, responses: { + lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'is_private', value: false} @@ -56,7 +56,7 @@ test.describe('Site password settings', async () => { await expect(section.getByText('Your site is not password protected')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'is_private', value: false} ] diff --git a/ghost/admin-x-settings/test/e2e/general/metadata.test.ts b/ghost/admin-x-settings/test/e2e/general/metadata.test.ts index cc517b8702..159593f19a 100644 --- a/ghost/admin-x-settings/test/e2e/general/metadata.test.ts +++ b/ghost/admin-x-settings/test/e2e/general/metadata.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Metadata settings', async () => { test('Supports editing metadata', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'meta_title', value: 'Alternative title'}, @@ -31,7 +31,7 @@ test.describe('Metadata settings', async () => { await expect(section.getByText('Alternative title')).toHaveCount(1); await expect(section.getByText('Alternative description')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'meta_title', value: 'Alternative title'}, {key: 'meta_description', value: 'Alternative description'} diff --git a/ghost/admin-x-settings/test/e2e/general/publicationLanguage.test.ts b/ghost/admin-x-settings/test/e2e/general/publicationLanguage.test.ts index 7cefc79ad7..0791ea8ea1 100644 --- a/ghost/admin-x-settings/test/e2e/general/publicationLanguage.test.ts +++ b/ghost/admin-x-settings/test/e2e/general/publicationLanguage.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Publication language settings', async () => { test('Supports editing the locale', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'locale', value: 'jp'} @@ -27,7 +27,7 @@ test.describe('Publication language settings', async () => { await expect(section.getByText('jp')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'locale', value: 'jp'} ] diff --git a/ghost/admin-x-settings/test/e2e/general/socialAccounts.test.ts b/ghost/admin-x-settings/test/e2e/general/socialAccounts.test.ts index e6b70c2057..35443b2d90 100644 --- a/ghost/admin-x-settings/test/e2e/general/socialAccounts.test.ts +++ b/ghost/admin-x-settings/test/e2e/general/socialAccounts.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Social account settings', async () => { test('Supports editing social URLs', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'facebook', value: 'fb'}, @@ -31,7 +31,7 @@ test.describe('Social account settings', async () => { await expect(section.getByText('https://www.facebook.com/fb')).toHaveCount(1); await expect(section.getByText('https://twitter.com/tw')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'facebook', value: 'fb'}, {key: 'twitter', value: '@tw'} diff --git a/ghost/admin-x-settings/test/e2e/general/timeZone.test.ts b/ghost/admin-x-settings/test/e2e/general/timeZone.test.ts index 3e29a31873..a9bc88a51b 100644 --- a/ghost/admin-x-settings/test/e2e/general/timeZone.test.ts +++ b/ghost/admin-x-settings/test/e2e/general/timeZone.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Time zone settings', async () => { test('Supports editing the time zone', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'timezone', value: 'Asia/Tokyo'} @@ -27,7 +27,7 @@ test.describe('Time zone settings', async () => { await expect(section.getByText('Asia/Tokyo')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'timezone', value: 'Asia/Tokyo'} ] diff --git a/ghost/admin-x-settings/test/e2e/general/titleAndDescription.test.ts b/ghost/admin-x-settings/test/e2e/general/titleAndDescription.test.ts index a0a334afc2..56802177d0 100644 --- a/ghost/admin-x-settings/test/e2e/general/titleAndDescription.test.ts +++ b/ghost/admin-x-settings/test/e2e/general/titleAndDescription.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Title and description settings', async () => { test('Supports editing the title and description', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'title', value: 'New Site Title'}, @@ -31,7 +31,7 @@ test.describe('Title and description settings', async () => { await expect(section.getByText('New Site Title')).toHaveCount(1); await expect(section.getByText('New Site Description')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'title', value: 'New Site Title'}, {key: 'description', value: 'New Site Description'} diff --git a/ghost/admin-x-settings/test/e2e/general/twitter.test.ts b/ghost/admin-x-settings/test/e2e/general/twitter.test.ts index 389de30222..5f45eaf9b0 100644 --- a/ghost/admin-x-settings/test/e2e/general/twitter.test.ts +++ b/ghost/admin-x-settings/test/e2e/general/twitter.test.ts @@ -3,7 +3,7 @@ import {mockApi} from '../../utils/e2e'; test.describe('Twitter settings', async () => { test('Supports editing the twitter card', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { images: { upload: {images: [{url: 'http://example.com/image.png', ref: null}]} } @@ -31,7 +31,7 @@ test.describe('Twitter settings', async () => { await expect(section.getByLabel('Twitter title')).toHaveCount(0); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'twitter_image', value: 'http://example.com/image.png'}, {key: 'twitter_title', value: 'Twititle'}, diff --git a/ghost/admin-x-settings/test/e2e/membership/access.test.ts b/ghost/admin-x-settings/test/e2e/membership/access.test.ts index 11a42a4286..0c6916d5f5 100644 --- a/ghost/admin-x-settings/test/e2e/membership/access.test.ts +++ b/ghost/admin-x-settings/test/e2e/membership/access.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Access settings', async () => { test('Supports editing access', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'default_content_visibility', value: 'members'}, @@ -35,7 +35,7 @@ test.describe('Access settings', async () => { await expect(section.getByText('Members only')).toHaveCount(1); await expect(section.getByText('All members')).toHaveCount(1); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'default_content_visibility', value: 'members'}, {key: 'members_signup_access', value: 'invite'}, diff --git a/ghost/admin-x-settings/test/e2e/membership/analytics.test.ts b/ghost/admin-x-settings/test/e2e/membership/analytics.test.ts index f3775dc039..bbb0d1a1ac 100644 --- a/ghost/admin-x-settings/test/e2e/membership/analytics.test.ts +++ b/ghost/admin-x-settings/test/e2e/membership/analytics.test.ts @@ -3,7 +3,7 @@ import {mockApi, updatedSettingsResponse} from '../../utils/e2e'; test.describe('Analytics settings', async () => { test('Supports toggling analytics settings', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { settings: { edit: updatedSettingsResponse([ {key: 'members_track_sources', value: false}, @@ -30,7 +30,7 @@ test.describe('Analytics settings', async () => { await section.getByRole('button', {name: 'Save'}).click(); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'members_track_sources', value: false}, {key: 'email_track_opens', value: false}, diff --git a/ghost/admin-x-settings/test/e2e/site/design.test.ts b/ghost/admin-x-settings/test/e2e/site/design.test.ts index f19d14c4fd..91897109d3 100644 --- a/ghost/admin-x-settings/test/e2e/site/design.test.ts +++ b/ghost/admin-x-settings/test/e2e/site/design.test.ts @@ -1,9 +1,9 @@ import {expect, test} from '@playwright/test'; import {mockApi} from '../../utils/e2e'; -test.describe('Theme settings', async () => { +test.describe('Design settings', async () => { test('Editing brand settings', async ({page}) => { - const lastApiRequest = await mockApi({page, responses: { + const lastApiRequests = await mockApi({page, responses: { previewHtml: { homepage: '
homepage preview
' } @@ -24,7 +24,9 @@ test.describe('Theme settings', async () => { await expect(modal).not.toBeVisible(); - expect(lastApiRequest.body).toEqual({ + expect(lastApiRequests.previewHtml.homepage.headers?.['x-ghost-preview']).toMatch(/&d=new\+description&/); + + expect(lastApiRequests.settings.edit.body).toEqual({ settings: [ {key: 'description', value: 'new description'} ] @@ -32,7 +34,24 @@ test.describe('Theme settings', async () => { }); test('Editing custom theme settings', async ({page}) => { - const lastApiRequest = await mockApi({page}); + const lastApiRequests = await mockApi({page, responses: { + custom_theme_settings: { + browse: { + custom_theme_settings: [{ + type: 'select', + options: [ + 'Logo on cover', + 'Logo in the middle', + 'Stacked' + ], + default: 'Logo on cover', + id: '648047658d265b0c8b33c591', + value: 'Stacked', + key: 'navigation_layout' + }] + } + } + }}); await page.goto('/'); @@ -49,13 +68,13 @@ test.describe('Theme settings', async () => { await expect(modal).not.toBeVisible(); - expect(lastApiRequest.body).toMatchObject({ + const expectedSettings = {navigation_layout: 'Logo in the middle'}; + const expectedEncoded = new URLSearchParams([['custom', JSON.stringify(expectedSettings)]]).toString(); + expect(lastApiRequests.previewHtml.homepage.headers?.['x-ghost-preview']).toMatch(new RegExp(`&${expectedEncoded.replace(/\+/g, '\\+')}`)); + + expect(lastApiRequests.custom_theme_settings.edit.body).toMatchObject({ custom_theme_settings: [ - {key: 'navigation_color'}, - {key: 'navigation_background_image'}, - {key: 'navigation_layout', value: 'Logo in the middle'}, - {key: 'show_publication_cover'}, - {key: 'email_signup_text'} + {key: 'navigation_layout', value: 'Logo in the middle'} ] }); }); diff --git a/ghost/admin-x-settings/test/e2e/site/navigation.test.ts b/ghost/admin-x-settings/test/e2e/site/navigation.test.ts new file mode 100644 index 0000000000..12e2052b1b --- /dev/null +++ b/ghost/admin-x-settings/test/e2e/site/navigation.test.ts @@ -0,0 +1,123 @@ +import {expect, test} from '@playwright/test'; +import {mockApi} from '../../utils/e2e'; + +test.describe('Navigation settings', async () => { + test('Editing primary and secondary navigation', async ({page}) => { + const lastApiRequests = await mockApi({page}); + + await page.goto('/'); + + const section = page.getByTestId('navigation'); + await section.getByRole('button', {name: 'Customize'}).click(); + + const modal = page.getByTestId('navigation-modal'); + + const primaryNavigationTab = modal.getByRole('tabpanel').first(); + + const primaryItem = primaryNavigationTab.getByTestId('navigation-item-editor').first(); + await primaryItem.getByLabel('Label').fill('existing item label'); + await primaryItem.getByLabel('URL').fill('/existing'); + + await primaryNavigationTab.getByTestId('new-navigation-item').getByLabel('Label').fill('new item label'); + await primaryNavigationTab.getByTestId('new-navigation-item').getByLabel('URL').fill('/new'); + await primaryNavigationTab.getByTestId('new-navigation-item').getByLabel('URL').blur(); + + await modal.getByRole('tab').last().click(); + + const secondaryNavigationTab = modal.getByRole('tabpanel').last(); + + const secondaryItem = secondaryNavigationTab.getByTestId('navigation-item-editor').first(); + await secondaryItem.getByLabel('Label').fill('existing item 2'); + await secondaryItem.getByLabel('URL').fill('/existing2'); + + await secondaryNavigationTab.getByTestId('new-navigation-item').getByLabel('Label').fill('new item 2'); + await secondaryNavigationTab.getByTestId('new-navigation-item').getByLabel('URL').press('Backspace'); + await secondaryNavigationTab.getByTestId('new-navigation-item').getByLabel('URL').fill('https://google.com'); + await secondaryNavigationTab.getByTestId('new-navigation-item').getByLabel('URL').blur(); + + await modal.getByRole('button', {name: 'OK'}).click(); + + await expect(modal).not.toBeVisible(); + + expect(lastApiRequests.settings.edit.body).toEqual({ + settings: [ + {key: 'navigation', value: '[{"url":"/existing/","label":"existing item label"},{"url":"/about/","label":"About"},{"url":"/new/","label":"new item label"}]'}, + {key: 'secondary_navigation', value: '[{"url":"/existing2/","label":"existing item 2"},{"url":"https://google.com","label":"new item 2"}]'} + ] + }); + }); + + test('Existing item validations', async ({page}) => { + await mockApi({page}); + + await page.goto('/'); + + const section = page.getByTestId('navigation'); + await section.getByRole('button', {name: 'Customize'}).click(); + + const modal = page.getByTestId('navigation-modal'); + + const primaryNavigationTab = modal.getByRole('tabpanel').first(); + + const primaryItem = primaryNavigationTab.getByTestId('navigation-item-editor').first(); + await primaryItem.getByLabel('Label').fill(''); + await primaryItem.getByLabel('URL').press('Backspace'); + await primaryItem.getByLabel('URL').fill('google.com'); + + await modal.getByRole('button', {name: 'OK'}).click(); + + await expect(primaryItem.getByText('You must specify a label')).toHaveCount(1); + await expect(primaryItem.getByText('You must specify a valid URL or relative path')).toHaveCount(1); + + await primaryItem.getByLabel('Label').press('A'); + await expect(primaryItem.getByText('You must specify a label')).toHaveCount(0); + + // The error should hide whenever the user types even if the URL is still not valid + await primaryItem.getByLabel('URL').press('A'); + await expect(primaryItem.getByText('You must specify a valid URL or relative path')).toHaveCount(0); + }); + + test('Adding a new item', async ({page}) => { + await mockApi({page}); + + await page.goto('/'); + + const section = page.getByTestId('navigation'); + await section.getByRole('button', {name: 'Customize'}).click(); + + const modal = page.getByTestId('navigation-modal'); + + const primaryNavigationTab = modal.getByRole('tabpanel').first(); + + await expect(primaryNavigationTab.getByTestId('navigation-item-editor')).toHaveCount(2); + + const newItem = primaryNavigationTab.getByTestId('new-navigation-item'); + await newItem.getByLabel('Label').fill(''); + await newItem.getByLabel('URL').press('Backspace'); + await newItem.getByLabel('URL').fill('google.com'); + + await newItem.getByTestId('add-button').click(); + + await expect(newItem.getByText('You must specify a label')).toHaveCount(1); + await expect(newItem.getByText('You must specify a valid URL or relative path')).toHaveCount(1); + + await newItem.getByLabel('Label').press('A'); + await expect(newItem.getByText('You must specify a label')).toHaveCount(0); + + // The error should hide whenever the user types even if the URL is still not valid + await newItem.getByLabel('URL').press('A'); + await expect(newItem.getByText('You must specify a valid URL or relative path')).toHaveCount(0); + + await newItem.getByLabel('Label').fill('Label'); + await newItem.getByLabel('URL').fill('https://google.com'); + + await newItem.getByTestId('add-button').click(); + + await expect(primaryNavigationTab.getByTestId('navigation-item-editor')).toHaveCount(3); + + await expect(primaryNavigationTab.getByTestId('navigation-item-editor').last().getByLabel('Label')).toHaveValue('Label'); + await expect(primaryNavigationTab.getByTestId('navigation-item-editor').last().getByLabel('URL')).toHaveValue('https://google.com/'); + await expect(newItem.getByLabel('Label')).toHaveValue(''); + await expect(newItem.getByLabel('URL')).toHaveValue('http://test.com/'); + }); +}); diff --git a/ghost/admin-x-settings/test/utils/e2e.ts b/ghost/admin-x-settings/test/utils/e2e.ts index 135432836e..02f4b9157b 100644 --- a/ghost/admin-x-settings/test/utils/e2e.ts +++ b/ghost/admin-x-settings/test/utils/e2e.ts @@ -1,11 +1,6 @@ -import {Page} from '@playwright/test'; +import {Page, Request} from '@playwright/test'; import {readFileSync} from 'fs'; -type LastApiRequest = { - url: null | string - body: null | any -}; - const responseFixtures = { settings: JSON.parse(readFileSync(`${__dirname}/responses/settings.json`).toString()), site: JSON.parse(readFileSync(`${__dirname}/responses/site.json`).toString()), @@ -27,85 +22,132 @@ interface Responses { browse?: any edit?: any } - previewHtml: { + previewHtml?: { homepage?: string } } +interface RequestRecord { + url?: string + body?: any + headers?: {[key: string]: string} +} + +type LastRequests = { + settings: { + browse: RequestRecord + edit: RequestRecord + } + site: { + browse: RequestRecord + } + images: { + upload: RequestRecord + } + custom_theme_settings: { + browse: RequestRecord + edit: RequestRecord + } + previewHtml: { + homepage: RequestRecord + } +}; + export async function mockApi({page,responses}: {page: Page, responses?: Responses}) { - const lastApiRequest: LastApiRequest = { - url: null, - body: null + const lastApiRequests: LastRequests = { + settings: {browse: {}, edit: {}}, + site: {browse: {}}, + images: {upload: {}}, + custom_theme_settings: {browse: {}, edit: {}}, + previewHtml: {homepage: {}} }; await mockApiResponse({ page, path: /\/ghost\/api\/admin\/settings\//, - responses: { - GET: {body: responses?.settings?.browse ?? responseFixtures.settings}, - PUT: {body: responses?.settings?.edit ?? responseFixtures.settings} - }, - lastApiRequest + respondTo: { + GET: { + body: responses?.settings?.browse ?? responseFixtures.settings, + updateLastRequest: lastApiRequests.settings.browse + }, + PUT: { + body: responses?.settings?.edit ?? responseFixtures.settings, + updateLastRequest: lastApiRequests.settings.edit + } + } }); await mockApiResponse({ page, path: /\/ghost\/api\/admin\/site\//, - responses: { - GET: {body: responses?.site?.browse ?? responseFixtures.site} - }, - lastApiRequest + respondTo: { + GET: { + body: responses?.site?.browse ?? responseFixtures.site, + updateLastRequest: lastApiRequests.site.browse + } + } }); await mockApiResponse({ page, path: /\/ghost\/api\/admin\/images\/upload\/$/, - responses: { - POST: {body: responses?.images?.upload ?? {images: [{url: 'http://example.com/image.png', ref: null}]}} - }, - lastApiRequest + respondTo: { + POST: { + body: responses?.images?.upload ?? {images: [{url: 'http://example.com/image.png', ref: null}]}, + updateLastRequest: lastApiRequests.images.upload + } + } }); await mockApiResponse({ page, path: /\/ghost\/api\/admin\/custom_theme_settings\/$/, - responses: { - GET: {body: responses?.custom_theme_settings?.browse ?? responseFixtures.custom_theme_settings}, - PUT: {body: responses?.custom_theme_settings?.edit ?? responseFixtures.custom_theme_settings} - }, - lastApiRequest - }); - - await page.route(responseFixtures.site.site.url, async (route) => { - if (!route.request().headers()['x-ghost-preview']) { - return route.continue(); + respondTo: { + GET: { + body: responses?.custom_theme_settings?.browse ?? responseFixtures.custom_theme_settings, + updateLastRequest: lastApiRequests.custom_theme_settings.browse + }, + PUT: { + body: responses?.custom_theme_settings?.edit ?? responseFixtures.custom_theme_settings, + updateLastRequest: lastApiRequests.custom_theme_settings.edit + } } - - await route.fulfill({ - status: 200, - body: responses?.previewHtml?.homepage ?? '
test
' - }); }); - return lastApiRequest; + await mockApiResponse({ + page, + path: responseFixtures.site.site.url, + respondTo: { + POST: { + condition: request => !!request.headers()['x-ghost-preview'], + body: responses?.previewHtml?.homepage ?? '
test
', + updateLastRequest: lastApiRequests.previewHtml.homepage + } + } + }); + + return lastApiRequests; } -interface MockResponse { +interface ResponseOptions { + condition?: (request: Request) => boolean body: any status?: number + updateLastRequest: RequestRecord } -async function mockApiResponse({page, path, lastApiRequest, responses}: { page: Page; path: string | RegExp; lastApiRequest: LastApiRequest, responses: { [method: string]: MockResponse } }) { +async function mockApiResponse({page, path, respondTo}: { page: Page; path: string | RegExp; respondTo: { [method: string]: ResponseOptions } }) { await page.route(path, async (route) => { - const response = responses[route.request().method()]; + const response = respondTo[route.request().method()]; - if (!response) { + if (!response || (response.condition && !response.condition(route.request()))) { return route.continue(); } const requestBody = JSON.parse(route.request().postData() || 'null'); - lastApiRequest.body = requestBody; - lastApiRequest.url = route.request().url(); + response.updateLastRequest.body = requestBody; + response.updateLastRequest.url = route.request().url(); + response.updateLastRequest.headers = route.request().headers(); await route.fulfill({ status: response.status || 200,