diff --git a/.gitignore b/.gitignore index 4887893825..d2553d65ff 100644 --- a/.gitignore +++ b/.gitignore @@ -133,6 +133,9 @@ Caddyfile # Signup Form and local environments /ghost/signup-form/umd /ghost/signup-form/.env*.local +/ghost/signup-form/test-results/ +/ghost/signup-form/playwright-report/ +/ghost/signup-form/playwright/.cache/ # Announcement-Bar /ghost/announcement-bar/umd diff --git a/ghost/signup-form/README.md b/ghost/signup-form/README.md index 93a8c8a244..38f763a8e2 100644 --- a/ghost/signup-form/README.md +++ b/ghost/signup-form/README.md @@ -34,3 +34,6 @@ Follow the instructions for the top-level repo. - `yarn lint` run just eslint - `yarn test` run lint and tests +- `yarn test:e2e` run e2e tests on Chromium +- `yarn test:slowmo` run e2e tests visually (headed) and slower on Chromium +- `yarn test:e2e:full` run e2e tests on all browsers diff --git a/ghost/signup-form/package.json b/ghost/signup-form/package.json index 38eafb42c2..b1c9327eaf 100644 --- a/ghost/signup-form/package.json +++ b/ghost/signup-form/package.json @@ -1,73 +1,78 @@ { - "name": "@tryghost/signup-form", - "version": "0.0.0", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/TryGhost/Ghost/tree/main/packages/signup-form" - }, - "author": "Ghost Foundation", - "files": [ - "LICENSE", - "README.md", - "umd/" - ], - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/" - }, - "scripts": { - "dev": "concurrently \"vite --port 6173\" \"vite preview -l silent\" \"vite build --watch\"", - "build": "tsc && vite build", - "lint": "yarn run lint:js", - "lint:js": "eslint --ext .js,.ts,.cjs,.tsx --cache src test", - "test:unit": "yarn build", - "preview": "vite preview", - "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build", - "preship": "yarn lint", - "ship": "STATUS=$(git status --porcelain); echo $STATUS; if [ -z \"$STATUS\" ]; then yarn version; fi", - "postship": "git push ${GHOST_UPSTREAM:-origin} --follow-tags && npm publish", - "prepublishOnly": "yarn build" - }, - "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@storybook/addon-essentials": "7.0.15", - "@storybook/addon-interactions": "7.0.15", - "@storybook/addon-links": "7.0.15", - "@storybook/addon-styling": "1.0.6", - "@storybook/blocks": "7.0.15", - "@storybook/react": "7.0.15", - "@storybook/react-vite": "7.0.15", - "@storybook/testing-library": "0.1.0", - "@tailwindcss/line-clamp": "0.4.4", - "@types/react": "18.0.28", - "@types/react-dom": "18.0.11", - "@typescript-eslint/eslint-plugin": "5.57.1", - "@typescript-eslint/parser": "5.57.1", - "@vitejs/plugin-react": "4.0.0", - "autoprefixer": "10.4.14", - "concurrently": "8.0.1", - "eslint": "8.38.0", - "eslint-config-react-app": "7.0.1", - "eslint-plugin-ghost": "2.18.0", - "eslint-plugin-react": "7.32.2", - "eslint-plugin-react-hooks": "4.6.0", - "eslint-plugin-react-refresh": "0.3.4", - "eslint-plugin-tailwindcss": "3.11.0", - "postcss": "8.4.23", - "postcss-import": "^15.1.0", - "prop-types": "15.8.1", - "rollup-plugin-node-builtins": "2.1.2", - "storybook": "7.0.15", - "stylelint": "15.6.1", - "tailwindcss": "3.3.2", - "typescript": "5.0.4", - "vite": "4.3.8", - "vite-plugin-svgr": "3.2.0", - "vitest": "0.31.1" - } + "name": "@tryghost/signup-form", + "version": "0.0.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/TryGhost/Ghost/tree/main/packages/signup-form" + }, + "author": "Ghost Foundation", + "files": [ + "LICENSE", + "README.md", + "umd/" + ], + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "scripts": { + "dev": "concurrently \"vite --port 6173\" \"vite preview -l silent\" \"vite build --watch\"", + "dev:test": "vite build && vite preview --port 6175", + "build": "tsc && vite build", + "lint": "yarn run lint:js", + "lint:js": "eslint --ext .js,.ts,.cjs,.tsx --cache src test", + "test:unit": "yarn build", + "test:e2e": "NODE_OPTIONS='--experimental-specifier-resolution=node --no-warnings' VITE_TEST=true playwright test", + "test:slowmo": "TIMEOUT=100000 PLAYWRIGHT_SLOWMO=100 yarn test:e2e --headed", + "test:e2e:full": "ALL_BROWSERS=1 yarn test:e2e", + "preview": "vite preview", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build", + "preship": "yarn lint", + "ship": "STATUS=$(git status --porcelain); echo $STATUS; if [ -z \"$STATUS\" ]; then yarn version; fi", + "postship": "git push ${GHOST_UPSTREAM:-origin} --follow-tags && npm publish", + "prepublishOnly": "yarn build" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@playwright/test": "1.34.2", + "@storybook/addon-essentials": "7.0.15", + "@storybook/addon-interactions": "7.0.15", + "@storybook/addon-links": "7.0.15", + "@storybook/addon-styling": "1.0.6", + "@storybook/blocks": "7.0.15", + "@storybook/react": "7.0.15", + "@storybook/react-vite": "7.0.15", + "@storybook/testing-library": "0.1.0", + "@tailwindcss/line-clamp": "0.4.4", + "@types/react": "18.0.28", + "@types/react-dom": "18.0.11", + "@typescript-eslint/eslint-plugin": "5.57.1", + "@typescript-eslint/parser": "5.57.1", + "@vitejs/plugin-react": "4.0.0", + "autoprefixer": "10.4.14", + "concurrently": "8.0.1", + "eslint": "8.38.0", + "eslint-config-react-app": "7.0.1", + "eslint-plugin-ghost": "2.18.0", + "eslint-plugin-react": "7.32.2", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-refresh": "0.3.4", + "eslint-plugin-tailwindcss": "3.11.0", + "postcss": "8.4.23", + "postcss-import": "^15.1.0", + "prop-types": "15.8.1", + "rollup-plugin-node-builtins": "2.1.2", + "storybook": "7.0.15", + "stylelint": "15.6.1", + "tailwindcss": "3.3.2", + "typescript": "5.0.4", + "vite": "4.3.8", + "vite-plugin-svgr": "3.2.0", + "vitest": "0.31.1" } +} diff --git a/ghost/signup-form/playwright.config.ts b/ghost/signup-form/playwright.config.ts new file mode 100644 index 0000000000..8df0a35ed1 --- /dev/null +++ b/ghost/signup-form/playwright.config.ts @@ -0,0 +1,57 @@ +import {defineConfig, devices} from '@playwright/test'; + +export const E2E_PORT = 6175; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './test/e2e', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + launchOptions: { + slowMo: parseInt(process.env.PLAYWRIGHT_SLOWMO ?? '') || 0, + // force GPU hardware acceleration + // (even in headless mode) + args: ['--use-gl=egl'] + } + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: {...devices['Desktop Chrome']} + }, + + ...(process.env.ALL_BROWSERS ? [{ + name: 'firefox', + use: {...devices['Desktop Firefox']} + }, + + { + name: 'webkit', + use: {...devices['Desktop Safari']} + }] : []) + ], + + /* Run local dev server before starting the tests */ + webServer: { + command: `yarn dev:test`, + url: `http://localhost:${E2E_PORT}/signup-form.min.js`, + reuseExistingServer: !process.env.CI, + timeout: 10000 + } +}); diff --git a/ghost/signup-form/test/e2e/form.test.ts b/ghost/signup-form/test/e2e/form.test.ts new file mode 100644 index 0000000000..8a012d08fe --- /dev/null +++ b/ghost/signup-form/test/e2e/form.test.ts @@ -0,0 +1,49 @@ +import {expect} from '@playwright/test'; +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.'}); + + // 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'}); + + // Check no title or description present + await expect(frame.getByRole('heading')).toHaveCount(0); + await expect(frame.getByRole('paragraph')).toHaveCount(0); + }); +}); + diff --git a/ghost/signup-form/test/hello.test.js b/ghost/signup-form/test/unit/hello.test.js similarity index 77% rename from ghost/signup-form/test/hello.test.js rename to ghost/signup-form/test/unit/hello.test.js index 3224ab57bf..d3fca19a16 100644 --- a/ghost/signup-form/test/hello.test.js +++ b/ghost/signup-form/test/unit/hello.test.js @@ -3,6 +3,6 @@ const assert = require('assert'); describe('Hello world', function () { it('Runs a test', function () { // TODO: Write me! - assert.ok(require('../index')); + assert.ok(require('../../index')); }); }); diff --git a/ghost/signup-form/test/utils/e2e.ts b/ghost/signup-form/test/utils/e2e.ts new file mode 100644 index 0000000000..8d82f12a5f --- /dev/null +++ b/ghost/signup-form/test/utils/e2e.ts @@ -0,0 +1,20 @@ +import {E2E_PORT} from '../../playwright.config'; + +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}); + + await page.evaluate((data) => { + const scriptTag = document.createElement('script'); + scriptTag.src = data.url; + + for (const option of Object.keys(data.options)) { + scriptTag.dataset[option] = data.options[option]; + } + document.body.appendChild(scriptTag); + }, {url, options}); + await page.waitForSelector('iframe'); + return page.frameLocator('iframe'); +} diff --git a/ghost/signup-form/test/utils/isTestEnv.js b/ghost/signup-form/test/utils/isTestEnv.js new file mode 100644 index 0000000000..f67b101760 --- /dev/null +++ b/ghost/signup-form/test/utils/isTestEnv.js @@ -0,0 +1 @@ +export const isTestEnv = import.meta.env.VITE_TEST === 'true';