mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-24 03:14:03 +03:00
Added e2e tests for most simple AdminX settings
This commit is contained in:
parent
089a3f7aaf
commit
885531b345
@ -45,6 +45,7 @@
|
||||
"dependencies": {
|
||||
"@ebay/nice-modal-react": "^1.2.10",
|
||||
"@tryghost/timezone-data": "0.3.0",
|
||||
"clsx": "^1.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"validator": "7.2.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useEffect, useId, useState} from 'react';
|
||||
|
||||
import Heading from './Heading';
|
||||
import Hint from './Hint';
|
||||
@ -20,6 +20,8 @@ interface SelectProps {
|
||||
}
|
||||
|
||||
const Select: React.FC<SelectProps> = ({title, prompt, options, onSelect, error, hint, defaultSelectedOption, clearBg = false}) => {
|
||||
const id = useId();
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState(defaultSelectedOption);
|
||||
|
||||
useEffect(() => {
|
||||
@ -36,9 +38,9 @@ const Select: React.FC<SelectProps> = ({title, prompt, options, onSelect, error,
|
||||
|
||||
return (
|
||||
<div className='flex flex-col'>
|
||||
{title && <Heading useLabelTag={true}>{title}</Heading>}
|
||||
{title && <Heading htmlFor={id} useLabelTag={true}>{title}</Heading>}
|
||||
<div className={`relative w-full after:pointer-events-none after:absolute ${clearBg ? 'after:right-0' : 'after:right-4'} after:block after:h-2 after:w-2 after:rotate-45 after:border-[1px] after:border-l-0 after:border-t-0 after:border-grey-900 after:content-[''] ${title ? 'after:top-[22px]' : 'after:top-[14px]'}`}>
|
||||
<select className={`w-full cursor-pointer appearance-none border-b ${!clearBg && 'bg-grey-75 px-[10px]'} py-2 outline-none ${error ? `border-red` : `border-grey-500 hover:border-grey-700 focus:border-black`} ${title && `mt-2`}`} value={selectedOption} onChange={handleOptionChange}>
|
||||
<select className={`w-full cursor-pointer appearance-none border-b ${!clearBg && 'bg-grey-75 px-[10px]'} py-2 outline-none ${error ? `border-red` : `border-grey-500 hover:border-grey-700 focus:border-black`} ${title && `mt-2`}`} id={id} value={selectedOption} onChange={handleOptionChange}>
|
||||
{prompt && <option value="">{prompt}</option>}
|
||||
{options.map(option => (
|
||||
<option
|
||||
@ -55,4 +57,4 @@ const Select: React.FC<SelectProps> = ({title, prompt, options, onSelect, error,
|
||||
);
|
||||
};
|
||||
|
||||
export default Select;
|
||||
export default Select;
|
||||
|
@ -1,13 +1,14 @@
|
||||
import React, {useId} from 'react';
|
||||
|
||||
import Heading from './Heading';
|
||||
import Hint from './Hint';
|
||||
import React, {useId} from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
type TextFieldType = 'text' | 'number' | 'email' | 'password' | 'file' | 'date' | 'time' | 'search';
|
||||
|
||||
interface TextFieldProps {
|
||||
inputRef?: React.RefObject<HTMLInputElement>;
|
||||
title?: string;
|
||||
hideTitle?: boolean;
|
||||
type?: TextFieldType;
|
||||
value?: string;
|
||||
error?: boolean;
|
||||
@ -24,6 +25,7 @@ const TextField: React.FC<TextFieldProps> = ({
|
||||
type = 'text',
|
||||
inputRef,
|
||||
title,
|
||||
hideTitle,
|
||||
value,
|
||||
error,
|
||||
placeholder,
|
||||
@ -39,10 +41,16 @@ const TextField: React.FC<TextFieldProps> = ({
|
||||
|
||||
return (
|
||||
<div className='flex flex-col'>
|
||||
{title && <Heading htmlFor={id} useLabelTag={true}>{title}</Heading>}
|
||||
{title && <Heading className={hideTitle ? 'sr-only' : ''} htmlFor={id} useLabelTag={true}>{title}</Heading>}
|
||||
<input
|
||||
ref={inputRef}
|
||||
className={`border-b ${clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]'} py-2 ${error ? `border-red` : `border-grey-500 hover:border-grey-700 focus:border-black`} ${(title && !clearBg) && `mt-2`} ${className}`}
|
||||
className={clsx(
|
||||
'border-b py-2',
|
||||
clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]',
|
||||
error ? `border-red` : `border-grey-500 hover:border-grey-700 focus:border-black`,
|
||||
(title && !hideTitle && !clearBg) && `mt-2`,
|
||||
className
|
||||
)}
|
||||
defaultValue={value}
|
||||
id={id}
|
||||
maxLength={maxLength}
|
||||
|
@ -107,13 +107,13 @@ const DefaultRecipients: React.FC = () => {
|
||||
defaultSelectedOption={emailRecipientValue}
|
||||
hint='Who should be able to subscribe to your site?'
|
||||
options={RECIPIENT_FILTER_OPTIONS}
|
||||
title="Subscription access"
|
||||
title="Default Newsletter recipients"
|
||||
onSelect={(value) => {
|
||||
setDefaultRecipientValue(value);
|
||||
}}
|
||||
/>
|
||||
{(emailRecipientValue === 'segment') && (
|
||||
<MultiSelect
|
||||
<MultiSelect
|
||||
defaultValues={[
|
||||
{value: 'option2', label: 'Fake tier 2'}
|
||||
]}
|
||||
@ -139,6 +139,7 @@ const DefaultRecipients: React.FC = () => {
|
||||
navid='default-recipients'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='default-recipients'
|
||||
title='Default recipients'
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -149,4 +150,4 @@ const DefaultRecipients: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default DefaultRecipients;
|
||||
export default DefaultRecipients;
|
||||
|
@ -100,6 +100,7 @@ const MailGun: React.FC = () => {
|
||||
navid='mailgun'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='mailgun'
|
||||
title='Mailgun'
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -110,4 +111,4 @@ const MailGun: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default MailGun;
|
||||
export default MailGun;
|
||||
|
@ -65,7 +65,7 @@ const Facebook: React.FC = () => {
|
||||
<ImageUpload
|
||||
fileUploadClassName='flex cursor-pointer items-center justify-center rounded rounded-b-none border border-grey-100 border-b-0 bg-grey-75 p-3 text-sm font-semibold text-grey-800 hover:text-black'
|
||||
height='300px'
|
||||
id='twitter-image'
|
||||
id='facebook-image'
|
||||
imageURL={facebookImage}
|
||||
onDelete={handleImageDelete}
|
||||
onUpload={handleImageUpload}
|
||||
@ -101,6 +101,7 @@ const Facebook: React.FC = () => {
|
||||
navid='facebook'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='facebook'
|
||||
title='Facebook card'
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -112,4 +113,4 @@ const Facebook: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Facebook;
|
||||
export default Facebook;
|
||||
|
@ -67,7 +67,9 @@ const LockSite: React.FC = () => {
|
||||
<TextField
|
||||
hint={hint}
|
||||
placeholder="Enter password"
|
||||
title="Site password"
|
||||
value={password}
|
||||
hideTitle
|
||||
onChange={handlePasswordChange}
|
||||
/>
|
||||
}
|
||||
@ -80,6 +82,7 @@ const LockSite: React.FC = () => {
|
||||
navid='locksite'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='locksite'
|
||||
title='Make site private'
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -90,4 +93,4 @@ const LockSite: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default LockSite;
|
||||
export default LockSite;
|
||||
|
@ -103,6 +103,7 @@ const Metadata: React.FC = () => {
|
||||
navid='metadata'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='metadata'
|
||||
title='Metadata'
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -119,4 +120,4 @@ const Metadata: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Metadata;
|
||||
export default Metadata;
|
||||
|
@ -58,6 +58,7 @@ const PublicationLanguage: React.FC = () => {
|
||||
navid='publication-language'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='publication-language'
|
||||
title="Publication Language"
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -68,4 +69,4 @@ const PublicationLanguage: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default PublicationLanguage;
|
||||
export default PublicationLanguage;
|
||||
|
@ -160,6 +160,7 @@ const SocialAccounts: React.FC = () => {
|
||||
navid='social-accounts'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='social-accounts'
|
||||
title='Social accounts'
|
||||
onCancel={handleCancel}
|
||||
onSave={() => {
|
||||
@ -191,4 +192,4 @@ const SocialAccounts: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SocialAccounts;
|
||||
export default SocialAccounts;
|
||||
|
@ -87,6 +87,7 @@ const TimeZone: React.FC = () => {
|
||||
navid='timezone'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='timezone'
|
||||
title='Site timezone'
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -97,4 +98,4 @@ const TimeZone: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default TimeZone;
|
||||
export default TimeZone;
|
||||
|
@ -103,6 +103,7 @@ const Twitter: React.FC = () => {
|
||||
navid='twitter'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='twitter'
|
||||
title='Twitter card'
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -114,4 +115,4 @@ const Twitter: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Twitter;
|
||||
export default Twitter;
|
||||
|
@ -103,6 +103,7 @@ const Access: React.FC = () => {
|
||||
navid='access'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='access'
|
||||
title='Access'
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -113,4 +114,4 @@ const Access: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Access;
|
||||
export default Access;
|
||||
|
@ -76,6 +76,7 @@ const Analytics: React.FC = () => {
|
||||
navid='analytics'
|
||||
saveState={saveState}
|
||||
state={currentState}
|
||||
testId='analytics'
|
||||
title='Analytics'
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
@ -89,4 +90,4 @@ const Analytics: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Analytics;
|
||||
export default Analytics;
|
||||
|
@ -0,0 +1,58 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
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: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'editor_default_email_recipients', value: 'filter'},
|
||||
{key: 'editor_default_email_recipients_filter', value: 'status:-free'}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('default-recipients');
|
||||
|
||||
await expect(section.getByText('Whoever has access to the post')).toHaveCount(1);
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
await section.getByLabel('Default newsletter recipients').selectOption('All members');
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'editor_default_email_recipients', value: 'filter'},
|
||||
{key: 'editor_default_email_recipients_filter', value: 'status:free,status:-free'}
|
||||
]
|
||||
});
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
await section.getByLabel('Default newsletter recipients').selectOption('Usually nobody');
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'editor_default_email_recipients', value: 'filter'},
|
||||
{key: 'editor_default_email_recipients_filter', value: null}
|
||||
]
|
||||
});
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
await section.getByLabel('Default newsletter recipients').selectOption('Paid-members only');
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('Default newsletter recipients')).toHaveCount(0);
|
||||
|
||||
await expect(section.getByText('Paid-members only')).toHaveCount(1);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'editor_default_email_recipients', value: 'filter'},
|
||||
{key: 'editor_default_email_recipients_filter', value: 'status:-free'}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
39
ghost/admin-x-settings/test/e2e/email/mailgun.test.ts
Normal file
39
ghost/admin-x-settings/test/e2e/email/mailgun.test.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
import {mockApi, updatedSettingsResponse} from '../../utils/e2e';
|
||||
|
||||
test.describe('Mailgun settings', async () => {
|
||||
test('Supports setting up mailgun', async ({page}) => {
|
||||
const lastApiRequest = await mockApi({page, responses: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'mailgun_domain', value: 'test.com'},
|
||||
{key: 'mailgun_api_key', value: 'test'}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('mailgun');
|
||||
|
||||
await expect(section.getByText('Mailgun is not set up')).toHaveCount(1);
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
await section.getByLabel('Mailgun domain').fill('test.com');
|
||||
await section.getByLabel('Mailgun private API key').fill('test');
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('Mailgun domain')).toHaveCount(0);
|
||||
|
||||
await expect(section.getByText('Mailgun is set up')).toHaveCount(1);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'mailgun_domain', value: 'test.com'},
|
||||
{key: 'mailgun_api_key', value: 'test'}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
42
ghost/admin-x-settings/test/e2e/general/facebook.test.ts
Normal file
42
ghost/admin-x-settings/test/e2e/general/facebook.test.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
import {mockApi} from '../../utils/e2e';
|
||||
|
||||
test.describe('Facebook settings', async () => {
|
||||
test('Supports editing the facebook card', async ({page}) => {
|
||||
const lastApiRequest = await mockApi({page, responses: {
|
||||
images: {
|
||||
upload: {images: [{url: 'http://example.com/image.png', ref: null}]}
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('facebook');
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
|
||||
await page.locator('label[for="facebook-image"]').click();
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(`${__dirname}/../../utils/images/image.png`);
|
||||
|
||||
await expect(section.locator('[style*="background-image"]')).toHaveCSS('background-image', 'url("http://example.com/image.png")');
|
||||
|
||||
await section.getByLabel('Facebook title').fill('Facetitle');
|
||||
await section.getByLabel('Facebook description').fill('Facescription');
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('Facebook title')).toHaveCount(0);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'og_image', value: 'http://example.com/image.png'},
|
||||
{key: 'og_title', value: 'Facetitle'},
|
||||
{key: 'og_description', value: 'Facescription'}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
65
ghost/admin-x-settings/test/e2e/general/lockSite.test.ts
Normal file
65
ghost/admin-x-settings/test/e2e/general/lockSite.test.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
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: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'is_private', value: true},
|
||||
{key: 'password', value: 'password'}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('locksite');
|
||||
|
||||
await expect(section.getByText('Your site is not password protected')).toHaveCount(1);
|
||||
|
||||
// Add a site password
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
await section.getByLabel(/Enable password protection/).check();
|
||||
await section.getByLabel('Site password').fill('password');
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('Site password')).toHaveCount(0);
|
||||
|
||||
await expect(section.getByText('Your site is password protected')).toHaveCount(1);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'is_private', value: true},
|
||||
{key: 'password', value: 'password'}
|
||||
]
|
||||
});
|
||||
|
||||
// Remove the site password
|
||||
|
||||
lastApiRequest = await mockApi({page, responses: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'is_private', value: false}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
await section.getByLabel(/Enable password protection/).uncheck();
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByText('Your site is not password protected')).toHaveCount(1);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'is_private', value: false}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
41
ghost/admin-x-settings/test/e2e/general/metadata.test.ts
Normal file
41
ghost/admin-x-settings/test/e2e/general/metadata.test.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
import {mockApi, updatedSettingsResponse} from '../../utils/e2e';
|
||||
|
||||
test.describe('Metadata settings', async () => {
|
||||
test('Supports editing metadata', async ({page}) => {
|
||||
const lastApiRequest = await mockApi({page, responses: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'meta_title', value: 'Alternative title'},
|
||||
{key: 'meta_description', value: 'Alternative description'}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('metadata');
|
||||
|
||||
await expect(section.getByText('Test Site')).toHaveCount(1);
|
||||
await expect(section.getByText('Thoughts, stories and ideas.')).toHaveCount(1);
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
await section.getByLabel('Meta title').fill('Alternative title');
|
||||
await section.getByLabel('Meta description').fill('Alternative description');
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('Meta title')).toHaveCount(0);
|
||||
|
||||
await expect(section.getByText('Alternative title')).toHaveCount(1);
|
||||
await expect(section.getByText('Alternative description')).toHaveCount(1);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'meta_title', value: 'Alternative title'},
|
||||
{key: 'meta_description', value: 'Alternative description'}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,36 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
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: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'locale', value: 'jp'}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('publication-language');
|
||||
|
||||
await expect(section.getByText('en')).toHaveCount(1);
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
await section.getByLabel('Site language').fill('jp');
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('Site language')).toHaveCount(0);
|
||||
|
||||
await expect(section.getByText('jp')).toHaveCount(1);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'locale', value: 'jp'}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
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: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'facebook', value: 'fb'},
|
||||
{key: 'twitter', value: '@tw'}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('social-accounts');
|
||||
|
||||
await expect(section.getByText('https://www.facebook.com/ghost')).toHaveCount(1);
|
||||
await expect(section.getByText('https://twitter.com/ghost')).toHaveCount(1);
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
await section.getByLabel(`URL of your publication's Facebook Page`).fill('https://www.facebook.com/fb');
|
||||
await section.getByLabel('URL of your Twitter profile').fill('https://twitter.com/tw');
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('URL of your Twitter profile')).toHaveCount(0);
|
||||
|
||||
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({
|
||||
settings: [
|
||||
{key: 'facebook', value: 'fb'},
|
||||
{key: 'twitter', value: '@tw'}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
36
ghost/admin-x-settings/test/e2e/general/timeZone.test.ts
Normal file
36
ghost/admin-x-settings/test/e2e/general/timeZone.test.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
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: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'timezone', value: 'Asia/Tokyo'}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('timezone');
|
||||
|
||||
await expect(section.getByText('Etc/UTC')).toHaveCount(1);
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
await section.getByLabel('Site timezone').selectOption('Asia/Tokyo');
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('Site timezone')).toHaveCount(0);
|
||||
|
||||
await expect(section.getByText('Asia/Tokyo')).toHaveCount(1);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'timezone', value: 'Asia/Tokyo'}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
import {mockApi, updatedSettingsResponse} from '../utils/e2e';
|
||||
import {mockApi, updatedSettingsResponse} from '../../utils/e2e';
|
||||
|
||||
test.describe('Title and description settings', async () => {
|
||||
test('Supports editing the title and description', async ({page}) => {
|
42
ghost/admin-x-settings/test/e2e/general/twitter.test.ts
Normal file
42
ghost/admin-x-settings/test/e2e/general/twitter.test.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
import {mockApi} from '../../utils/e2e';
|
||||
|
||||
test.describe('Twitter settings', async () => {
|
||||
test('Supports editing the twitter card', async ({page}) => {
|
||||
const lastApiRequest = await mockApi({page, responses: {
|
||||
images: {
|
||||
upload: {images: [{url: 'http://example.com/image.png', ref: null}]}
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('twitter');
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
|
||||
await page.locator('label[for="twitter-image"]').click();
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(`${__dirname}/../../utils/images/image.png`);
|
||||
|
||||
await expect(section.locator('[style*="background-image"]')).toHaveCSS('background-image', 'url("http://example.com/image.png")');
|
||||
|
||||
await section.getByLabel('Twitter title').fill('Twititle');
|
||||
await section.getByLabel('Twitter description').fill('Twitscription');
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('Twitter title')).toHaveCount(0);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'twitter_image', value: 'http://example.com/image.png'},
|
||||
{key: 'twitter_title', value: 'Twititle'},
|
||||
{key: 'twitter_description', value: 'Twitscription'}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
46
ghost/admin-x-settings/test/e2e/membership/access.test.ts
Normal file
46
ghost/admin-x-settings/test/e2e/membership/access.test.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
import {mockApi, updatedSettingsResponse} from '../../utils/e2e';
|
||||
|
||||
test.describe('Access settings', async () => {
|
||||
test('Supports editing access', async ({page}) => {
|
||||
const lastApiRequest = await mockApi({page, responses: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'default_content_visibility', value: 'members'},
|
||||
{key: 'members_signup_access', value: 'invite'},
|
||||
{key: 'comments_enabled', value: 'all'}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('access');
|
||||
|
||||
await expect(section.getByText('Anyone can sign up')).toHaveCount(1);
|
||||
await expect(section.getByText('Public')).toHaveCount(1);
|
||||
await expect(section.getByText('Nobody')).toHaveCount(1);
|
||||
|
||||
await section.getByRole('button', {name: 'Edit'}).click();
|
||||
|
||||
await section.getByLabel('Subscription access').selectOption({label: 'Only people I invite'});
|
||||
await section.getByLabel('Default post access').selectOption({label: 'Members only'});
|
||||
await section.getByLabel('Commenting').selectOption({label: 'All members'});
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
await expect(section.getByLabel('Subscription access')).toHaveCount(0);
|
||||
|
||||
await expect(section.getByText('Only people I invite')).toHaveCount(1);
|
||||
await expect(section.getByText('Members only')).toHaveCount(1);
|
||||
await expect(section.getByText('All members')).toHaveCount(1);
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'default_content_visibility', value: 'members'},
|
||||
{key: 'members_signup_access', value: 'invite'},
|
||||
{key: 'comments_enabled', value: 'all'}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
42
ghost/admin-x-settings/test/e2e/membership/analytics.test.ts
Normal file
42
ghost/admin-x-settings/test/e2e/membership/analytics.test.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import {expect, test} from '@playwright/test';
|
||||
import {mockApi, updatedSettingsResponse} from '../../utils/e2e';
|
||||
|
||||
test.describe('Analytics settings', async () => {
|
||||
test('Supports toggling analytics settings', async ({page}) => {
|
||||
const lastApiRequest = await mockApi({page, responses: {
|
||||
settings: {
|
||||
edit: updatedSettingsResponse([
|
||||
{key: 'members_track_sources', value: false},
|
||||
{key: 'email_track_opens', value: false},
|
||||
{key: 'email_track_clicks', value: false},
|
||||
{key: 'outbound_link_tagging', value: false}
|
||||
])
|
||||
}
|
||||
}});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const section = page.getByTestId('analytics');
|
||||
|
||||
await expect(section.getByLabel(/Newsletter opens/)).toBeChecked();
|
||||
await expect(section.getByLabel(/Newsletter clicks/)).toBeChecked();
|
||||
await expect(section.getByLabel(/Member sources/)).toBeChecked();
|
||||
await expect(section.getByLabel(/Outbound link tagging/)).toBeChecked();
|
||||
|
||||
await section.getByLabel(/Newsletter opens/).uncheck();
|
||||
await section.getByLabel(/Newsletter clicks/).uncheck();
|
||||
await section.getByLabel(/Member sources/).uncheck();
|
||||
await section.getByLabel(/Outbound link tagging/).uncheck();
|
||||
|
||||
await section.getByRole('button', {name: 'Save'}).click();
|
||||
|
||||
expect(lastApiRequest.body).toEqual({
|
||||
settings: [
|
||||
{key: 'members_track_sources', value: false},
|
||||
{key: 'email_track_opens', value: false},
|
||||
{key: 'email_track_clicks', value: false},
|
||||
{key: 'outbound_link_tagging', value: false}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
@ -19,6 +19,9 @@ interface Responses {
|
||||
site?: {
|
||||
browse?: any
|
||||
}
|
||||
images?: {
|
||||
upload?: any
|
||||
}
|
||||
}
|
||||
|
||||
export async function mockApi({page,responses}: {page: Page, responses?: Responses}) {
|
||||
@ -51,6 +54,14 @@ export async function mockApi({page,responses}: {page: Page, responses?: Respons
|
||||
lastApiRequest
|
||||
});
|
||||
|
||||
await mockApiResponse({
|
||||
page,
|
||||
path: /\/ghost\/api\/admin\/images\/upload\/$/,
|
||||
method: 'POST',
|
||||
response: responses?.images?.upload ?? {images: [{url: 'http://example.com/image.png', ref: null}]},
|
||||
lastApiRequest
|
||||
});
|
||||
|
||||
return lastApiRequest;
|
||||
}
|
||||
|
||||
@ -71,7 +82,7 @@ async function mockApiResponse({page, path, method, response, lastApiRequest}: {
|
||||
});
|
||||
}
|
||||
|
||||
export function updatedSettingsResponse(newSettings: Array<{ key: string, value: string }>) {
|
||||
export function updatedSettingsResponse(newSettings: Array<{ key: string, value: string | boolean | null }>) {
|
||||
return {
|
||||
...responseFixtures.settings,
|
||||
settings: responseFixtures.settings.settings.map((setting) => {
|
||||
|
BIN
ghost/admin-x-settings/test/utils/images/image.png
Normal file
BIN
ghost/admin-x-settings/test/utils/images/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
@ -12782,6 +12782,11 @@ cloneable-readable@^1.0.0:
|
||||
process-nextick-args "^2.0.0"
|
||||
readable-stream "^2.3.5"
|
||||
|
||||
clsx@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
|
||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||
|
||||
cluster-key-slot@1.1.2, cluster-key-slot@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac"
|
||||
|
Loading…
Reference in New Issue
Block a user