Added E2E tests for API in signup form

fixes https://github.com/TryGhost/Team/issues/3330
This commit is contained in:
Simon Backx 2023-05-30 11:57:56 +02:00
parent 73e8e9837b
commit dbcdc8219d
4 changed files with 207 additions and 39 deletions

View File

@ -59,10 +59,10 @@ const Form: React.FC = () => {
return (
<div>
<form className='flex' onSubmit={submit}>
<input className={'flex-1 p-3 border ' + borderStyle} disabled={loading} placeholder='jamie@example.com' type="text" value={email} onChange={e => setEmail(e.target.value)}/>
<button className='bg-accent p-3 text-white' disabled={loading} type='submit'>Subscribe</button>
<input className={'flex-1 p-3 border ' + borderStyle} data-testid="input" disabled={loading} placeholder='jamie@example.com' type="text" value={email} onChange={e => setEmail(e.target.value)}/>
<button className='bg-accent p-3 text-white' data-testid="button" disabled={loading} type='submit'>Subscribe</button>
</form>
{error && <p className='pt-0.5 text-red-500'>{error}</p>}
{error && <p className='pt-0.5 text-red-500' data-testid="error-message">{error}</p>}
</div>
);
};

View File

@ -14,7 +14,7 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({email}) => {
<h1 className="text-xl font-bold">Now check your email!</h1>
</div>;
}
return <div className='bg-grey-300 p-24'>
return <div className='bg-grey-300 p-24' data-testid="success-page">
<h1 className="text-4xl font-bold">Now check your email!</h1>
<p className='pb-3'>An email has been send to {email}.</p>
</div>;

View File

@ -3,47 +3,182 @@ import {initialize} from '../utils/e2e';
import {test} from '@playwright/test';
test.describe('Form', async () => {
test('Displays the title', async ({page}) => {
const frame = await initialize({page, title: 'Sign up to get the latest news and updates.'});
test.describe('Display options', () => {
test('Displays the title', async ({page}) => {
const {frame} = await initialize({page, title: 'Sign up to get the latest news and updates.'});
// Check the Frame
const h1 = frame.getByRole('heading');
expect(await h1.innerText()).toBe('Sign up to get the latest news and updates.');
});
test('Displays the description', async ({page}) => {
const frame = await initialize({page, title: 'Title', description: 'Sign up to get the latest news and updates.'});
// Check the Frame
const p = frame.getByRole('paragraph');
expect(await p.innerText()).toBe('Sign up to get the latest news and updates.');
});
test('Uses the accent color', async ({page}) => {
// Need rgb notation here, because getComputedStyle returns rgb notation
const color = 'rgb(255, 123, 0)';
const frame = await initialize({page, color});
const submitButton = frame.getByRole('button');
// Check calculated background color of the button
const backgroundColor = await submitButton.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
// Check the Frame
const h1 = frame.getByRole('heading');
expect(await h1.innerText()).toBe('Sign up to get the latest news and updates.');
});
test('Displays the description', async ({page}) => {
const {frame} = await initialize({page, title: 'Title', description: 'Sign up to get the latest news and updates.'});
// Check the Frame
const p = frame.getByRole('paragraph');
expect(await p.innerText()).toBe('Sign up to get the latest news and updates.');
});
test('Uses the accent color', async ({page}) => {
// Need rgb notation here, because getComputedStyle returns rgb notation
const color = 'rgb(255, 123, 0)';
const {frame} = await initialize({page, color});
const submitButton = frame.getByRole('button');
// Check calculated background color of the button
const backgroundColor = await submitButton.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe(color);
});
test('Has a minimal style when title is missing', async ({page}) => {
let {frame} = await initialize({page});
// Check no title or description present
await expect(frame.getByRole('heading')).toHaveCount(0);
await expect(frame.getByRole('paragraph')).toHaveCount(0);
frame = (await initialize({page, description: 'Ignored'})).frame;
// Check no title or description present
await expect(frame.getByRole('heading')).toHaveCount(0);
await expect(frame.getByRole('paragraph')).toHaveCount(0);
});
expect(backgroundColor).toBe(color);
});
test('Has a minimal style when title is missing', async ({page}) => {
let frame = await initialize({page});
test.describe('Submitting', () => {
test('Can submit the form', async ({page}) => {
const {frame, lastApiRequest} = await initialize({page, title: 'Sign up'});
// Check no title or description present
await expect(frame.getByRole('heading')).toHaveCount(0);
await expect(frame.getByRole('paragraph')).toHaveCount(0);
// Fill out the form
const emailInput = frame.getByTestId('input');
await emailInput.fill('jamie@example.com');
frame = await initialize({page, description: 'Ignored'});
// Click the submit button
const submitButton = frame.getByTestId('button');
await submitButton.click();
// Check no title or description present
await expect(frame.getByRole('heading')).toHaveCount(0);
await expect(frame.getByRole('paragraph')).toHaveCount(0);
// Check input and button are gone
await expect(frame.getByTestId('input')).toHaveCount(0);
await expect(frame.getByTestId('button')).toHaveCount(0);
// Showing the success page
await expect(frame.getByTestId('success-page')).toHaveCount(1);
// Check email address text is visible on the page
await expect(frame.getByText('jamie@example.com')).toBeVisible();
// Check the request body
expect(lastApiRequest.body).not.toBeNull();
expect(lastApiRequest.body).toHaveProperty('labels', []);
expect(lastApiRequest.body).toHaveProperty('email', 'jamie@example.com');
});
test('Send a label when submitting the form', async ({page}) => {
const {frame, lastApiRequest} = await initialize({page, title: 'Sign up', labels: 'Hello world'});
// Fill out the form
const emailInput = frame.getByTestId('input');
await emailInput.fill('jamie@example.com');
// Click the submit button
const submitButton = frame.getByTestId('button');
await submitButton.click();
// Showing the success page
await expect(frame.getByTestId('success-page')).toHaveCount(1);
// Check the request body
expect(lastApiRequest.body).not.toBeNull();
expect(lastApiRequest.body).toHaveProperty('labels', ['Hello world']);
expect(lastApiRequest.body).toHaveProperty('email', 'jamie@example.com');
});
test('Send multiple labels when submitting the form', async ({page}) => {
const {frame, lastApiRequest} = await initialize({page, title: 'Sign up', labels: 'Hello world,and another one'});
// Fill out the form
const emailInput = frame.getByTestId('input');
await emailInput.fill('hey@example.com');
// Click the submit button
const submitButton = frame.getByTestId('button');
await submitButton.click();
// Showing the success page
await expect(frame.getByTestId('success-page')).toHaveCount(1);
// Check the request body
expect(lastApiRequest.body).not.toBeNull();
expect(lastApiRequest.body).toHaveProperty('labels', ['Hello world', 'and another one']);
expect(lastApiRequest.body).toHaveProperty('email', 'hey@example.com');
});
test('Cannot submit the form with invalid email address', async ({page}) => {
const {frame, lastApiRequest} = await initialize({page, title: 'Sign up'});
// Fill out the form
const emailInput = frame.getByTestId('input');
await emailInput.fill('invalid');
// Click the submit button
const submitButton = frame.getByTestId('button');
await submitButton.click();
// Check input and button are not gone
await expect(frame.getByTestId('input')).toHaveCount(1);
await expect(frame.getByTestId('button')).toHaveCount(1);
// Not showing the success page
await expect(frame.getByTestId('success-page')).toHaveCount(0);
// Check error message is visible on the page
const errorMessage = frame.getByTestId('error-message');
await expect(errorMessage).toHaveCount(1);
expect(await errorMessage.innerText()).toBe('Please enter a valid email address');
expect(lastApiRequest.body).toBeNull();
// Try again
await emailInput.fill('valid@example.com');
await submitButton.click();
// Check input and button are gone
await expect(frame.getByTestId('input')).toHaveCount(0);
await expect(frame.getByTestId('button')).toHaveCount(0);
// Showing the success page
await expect(frame.getByTestId('success-page')).toHaveCount(1);
// Check email address text is visible on the page
await expect(frame.getByText('valid@example.com')).toBeVisible();
});
test('Shows error message on network issues', async ({page}) => {
const {frame} = await initialize({page, title: 'Sign up', site: '127.0.0.1:9999'});
// Fill out the form
const emailInput = frame.getByTestId('input');
await emailInput.fill('valid@example.com');
// Click the submit button
const submitButton = frame.getByTestId('button');
await submitButton.click();
// Check input and button are not gone
await expect(frame.getByTestId('input')).toHaveCount(1);
await expect(frame.getByTestId('button')).toHaveCount(1);
// Not showing the success page
await expect(frame.getByTestId('success-page')).toHaveCount(0);
// Check error message is visible on the page
const errorMessage = frame.getByTestId('error-message');
await expect(errorMessage).toHaveCount(1);
expect(await errorMessage.innerText()).toBe('Something went wrong, please try again.');
});
});
});

View File

@ -1,10 +1,21 @@
import {E2E_PORT} from '../../playwright.config';
const MOCKED_SITE_URL = 'http://localhost:1234';
type LastApiRequest = {
body: null | any
};
export async function initialize({page, ...options}: {page: any; title?: string, description?: string, logo?: string, color?: string, site?: string, labels?: string}) {
const url = `http://localhost:${E2E_PORT}/signup-form.min.js`;
await page.goto('about:blank');
await page.setViewportSize({width: 1000, height: 1000});
const lastApiRequest = await mockApi({page});
if (!options.site) {
options.site = MOCKED_SITE_URL;
}
await page.evaluate((data) => {
const scriptTag = document.createElement('script');
@ -16,5 +27,27 @@ export async function initialize({page, ...options}: {page: any; title?: string,
document.body.appendChild(scriptTag);
}, {url, options});
await page.waitForSelector('iframe');
return page.frameLocator('iframe');
return {
frame: page.frameLocator('iframe'),
lastApiRequest
};
}
export async function mockApi({page}: {page: any}) {
const lastApiRequest: LastApiRequest = {
body: null
};
await page.route(`${MOCKED_SITE_URL}/members/api/send-magic-link/`, async (route) => {
const requestBody = JSON.parse(route.request().postData());
lastApiRequest.body = requestBody;
await route.fulfill({
status: 200,
body: 'ok'
});
});
return lastApiRequest;
}