From 6ef5e6a7e08ffd6fb2987a0b75877b44d1864d98 Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 6 Dec 2022 17:30:07 +0700 Subject: [PATCH] Fixed local environment for playwright tests refs https://github.com/TryGhost/Toolbox/issues/479 - this includes a handful of improvements to get Playwright working on a local environment including: - adding `testing-browser` environment so we don't nuke `development` environments, and makes all the necessary changes to get Ghost to behave when this is running - stopped running one global instance of Ghost as this doesn't provide a clean environment - copies a few default fixtures that are needed for the new environment --- ghost/core/core/cli/record-test.js | 2 +- .../core/server/services/stripe/service.js | 3 +- .../config/env/config.testing-browser.json | 63 +++ ghost/core/package.json | 5 +- ghost/core/playwright.config.js | 24 +- .../test/e2e-browser/utils/global-setup.js | 20 + .../test/e2e-browser/utils/global-teardown.js | 8 + ghost/core/test/utils/e2e-framework.js | 3 +- .../fixtures/default-settings-browser.json | 484 ++++++++++++++++++ 9 files changed, 585 insertions(+), 27 deletions(-) create mode 100644 ghost/core/core/shared/config/env/config.testing-browser.json create mode 100644 ghost/core/test/e2e-browser/utils/global-teardown.js create mode 100644 ghost/core/test/utils/fixtures/default-settings-browser.json diff --git a/ghost/core/core/cli/record-test.js b/ghost/core/core/cli/record-test.js index 29ae2772d1..217fd6c4f8 100644 --- a/ghost/core/core/cli/record-test.js +++ b/ghost/core/core/cli/record-test.js @@ -11,7 +11,7 @@ module.exports = class RecordTest extends Command { } permittedEnvironments() { - return ['development', 'testing']; + return ['testing-browser']; } async handle(argv) { diff --git a/ghost/core/core/server/services/stripe/service.js b/ghost/core/core/server/services/stripe/service.js index 9a07b87f8e..11ea6c0e03 100644 --- a/ghost/core/core/server/services/stripe/service.js +++ b/ghost/core/core/server/services/stripe/service.js @@ -13,7 +13,8 @@ const settingsHelpers = require('../settings-helpers'); async function configureApi() { const cfg = getConfig({settingsHelpers, config, urlUtils}); if (cfg) { - cfg.testEnv = process.env.NODE_ENV.startsWith('test'); + // @NOTE: to not start test mode when running playwright suite + cfg.testEnv = process.env.NODE_ENV.startsWith('test') && process.env.NODE_ENV !== 'testing-browser'; await module.exports.configure(cfg); return true; } diff --git a/ghost/core/core/shared/config/env/config.testing-browser.json b/ghost/core/core/shared/config/env/config.testing-browser.json new file mode 100644 index 0000000000..6a8ca406f4 --- /dev/null +++ b/ghost/core/core/shared/config/env/config.testing-browser.json @@ -0,0 +1,63 @@ +{ + "url": "http://127.0.0.1:2369", + "database": { + "client": "sqlite3", + "connection": { + "filename": "/tmp/ghost-test.db" + }, + "useNullAsDefault": true, + "debug": false + }, + "server": { + "port": 2369 + }, + "logging": { + "level": "fatal" + }, + "spam": { + "user_login": { + "minWait": 600000, + "maxWait": 604800000, + "freeRetries": 3 + }, + "user_reset": { + "minWait": 3600000, + "maxWait": 3600000, + "lifetime": 3600, + "freeRetries": 4 + }, + "global_reset": { + "minWait": 3600000, + "maxWait": 3600000, + "lifetime": 3600, + "freeRetries":4 + }, + "global_block": { + "minWait": 3600000, + "maxWait": 3600000, + "lifetime": 3600, + "freeRetries":4 + }, + "private_block": { + "minWait": 3600000, + "maxWait": 3600000, + "lifetime": 3600, + "freeRetries":99 + } + }, + "privacy": { + "useTinfoil": true, + "useStructuredData": true + }, + "useMinFiles": false, + "paths": { + "fixtures": "test/utils/fixtures/fixtures", + "defaultSettings": "test/utils/fixtures/default-settings-browser.json", + "urlCache": "test/utils/fixtures/urls" + }, + "sodoSearch": { + "url": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/sodo-search.min.js", + "styles": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/main.css", + "version": "1.0" + } +} diff --git a/ghost/core/package.json b/ghost/core/package.json index 2acec088cc..1b0657708e 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -32,8 +32,9 @@ "test:integration": "mocha --require=./test/utils/overrides.js --exit --trace-warnings --recursive --extension=test.js './test/integration' --timeout=10000", "test:e2e": "mocha --require=./test/utils/overrides.js --exit --trace-warnings --recursive --extension=test.js './test/e2e-api' './test/e2e-frontend' './test/e2e-server' './test/e2e-webhooks' --timeout=15000", "test:regression": "mocha --require=./test/utils/overrides.js --exit --trace-warnings --recursive --extension=test.js './test/regression' --timeout=60000", - "test:browser": "playwright test test/e2e-browser", - "test:browser:record": "NODE_ENV=development yarn start record-test", + "test:browser": "NODE_ENV=testing-browser playwright test test/e2e-browser", + "test:browser:setup": "npx playwright install", + "test:browser:record": "NODE_ENV=testing-browser yarn start record-test", "test:ci": "c8 -c ./.c8rc.e2e.json yarn test:ci:base", "test:ci:base": "yarn test:e2e -b && yarn test:integration -b && yarn test:regression -b", "test:unit:slow": "yarn test:unit --reporter=mocha-slow-test-reporter", diff --git a/ghost/core/playwright.config.js b/ghost/core/playwright.config.js index 5023753ce8..95e5982653 100644 --- a/ghost/core/playwright.config.js +++ b/ghost/core/playwright.config.js @@ -1,11 +1,3 @@ -const {execSync} = require('child_process'); - -const getWebhookSecret = () => { - const command = `stripe listen --print-secret ${process.env.CI ? `--api-key ${process.env.STRIPE_SECRET_KEY}` : ''}`.trim(); - const webhookSecret = execSync(command); - return webhookSecret.toString().trim(); -}; - /** @type {import('@playwright/test').PlaywrightTestConfig} */ const config = { @@ -19,20 +11,8 @@ const config = { // TODO: Where to put this storageState: 'playwright-state.json' }, - globalSetup: './test/e2e-browser/utils/global-setup' + globalSetup: './test/e2e-browser/utils/global-setup', + globalTeardown: './test/e2e-browser/utils/global-teardown' }; -if (!process.env.TEST_URL) { - config.webServer = { - // TODO: Replace yarn start - command: 'yarn start', - env: { - NODE_ENV: 'development', - WEBHOOK_SECRET: getWebhookSecret() - }, - reuseExistingServer: false, - url: 'http://localhost:2369' - }; -} - module.exports = config; diff --git a/ghost/core/test/e2e-browser/utils/global-setup.js b/ghost/core/test/e2e-browser/utils/global-setup.js index 684e8d73b1..c2da9362a4 100644 --- a/ghost/core/test/e2e-browser/utils/global-setup.js +++ b/ghost/core/test/e2e-browser/utils/global-setup.js @@ -5,12 +5,20 @@ const {knex} = require('../../../core/server/data/db'); const {setupGhost, setupStripe} = require('./e2e-browser-utils'); const {chromium} = require('@playwright/test'); const models = require('../../../core/server/models'); +const {startGhost} = require('../../utils/e2e-framework'); +const {stopGhost} = require('../../utils/e2e-utils'); const startWebhookServer = () => { const command = `stripe listen --forward-to ${config.getSiteUrl()}/members/webhooks/stripe/ ${process.env.CI ? `--api-key ${process.env.STRIPE_SECRET_KEY}` : ''}`.trim(); spawn(command.split(' ')[0], command.split(' ').slice(1)); }; +const getWebhookSecret = async () => { + const command = `stripe listen --print-secret ${process.env.CI ? `--api-key ${process.env.STRIPE_SECRET_KEY}` : ''}`.trim(); + const webhookSecret = (await promisify(exec)(command)).stdout; + return webhookSecret.toString().trim(); +}; + const generateStripeIntegrationToken = async () => { const inquirer = require('inquirer'); const stripeDatabaseKeys = { @@ -51,6 +59,14 @@ const setup = async (playwrightConfig) => { if (!usingRemoteServer) { startWebhookServer(); stripeConnectIntegrationToken = await generateStripeIntegrationToken(); + + process.env.WEBHOOK_SECRET = await getWebhookSecret(); + + await startGhost({ + frontend: true, + server: true, + backend: true + }); } const {baseURL, storageState} = playwrightConfig.projects[0].use; @@ -64,6 +80,10 @@ const setup = async (playwrightConfig) => { } await page.context().storageState({path: storageState}); await browser.close(); + + if (!usingRemoteServer) { + await stopGhost(); + } }; module.exports = setup; diff --git a/ghost/core/test/e2e-browser/utils/global-teardown.js b/ghost/core/test/e2e-browser/utils/global-teardown.js new file mode 100644 index 0000000000..10d9852a91 --- /dev/null +++ b/ghost/core/test/e2e-browser/utils/global-teardown.js @@ -0,0 +1,8 @@ +/** + * Teardown the environment + */ +const teardown = async (playwrightConfig) => { + // @NOTE: local environment should probably drop the db state here +}; + +module.exports = teardown; diff --git a/ghost/core/test/utils/e2e-framework.js b/ghost/core/test/utils/e2e-framework.js index cb6d70a6e0..8889b2251c 100644 --- a/ghost/core/test/utils/e2e-framework.js +++ b/ghost/core/test/utils/e2e-framework.js @@ -385,7 +385,8 @@ module.exports = { getGhostAPIAgent, getAgentsWithFrontend }, - + // @NOTE: startGhost only exposed for playwright tests + startGhost, // Mocks and Stubs mockManager, diff --git a/ghost/core/test/utils/fixtures/default-settings-browser.json b/ghost/core/test/utils/fixtures/default-settings-browser.json new file mode 100644 index 0000000000..c4b941f60d --- /dev/null +++ b/ghost/core/test/utils/fixtures/default-settings-browser.json @@ -0,0 +1,484 @@ +{ + "core": { + "db_hash": { + "defaultValue": null, + "type": "string" + }, + "routes_hash": { + "defaultValue": null, + "type": "string" + }, + "next_update_check": { + "defaultValue": null, + "type": "number" + }, + "notifications": { + "defaultValue": "[]", + "type": "array" + }, + "version_notifications": { + "defaultValue": "[]", + "type": "array" + }, + "admin_session_secret": { + "defaultValue": null, + "type": "string" + }, + "theme_session_secret": { + "defaultValue": null, + "type": "string" + }, + "ghost_public_key": { + "defaultValue": null, + "type": "string" + }, + "ghost_private_key": { + "defaultValue": null, + "type": "string" + }, + "members_public_key": { + "defaultValue": null, + "type": "string" + }, + "members_private_key": { + "defaultValue": null, + "type": "string" + }, + "members_email_auth_secret": { + "defaultValue": null, + "type": "string" + }, + "members_stripe_webhook_id": { + "defaultValue": null, + "type": "string" + }, + "members_stripe_webhook_secret": { + "defaultValue": null, + "type": "string" + } + }, + "site": { + "title": { + "defaultValue": "Ghost", + "validations": { + "isLength": { + "max": 150 + } + }, + "flags": "PUBLIC", + "type": "string" + }, + "description": { + "defaultValue": "Thoughts, stories and ideas", + "validations": { + "isLength": { + "max": 200 + } + }, + "flags": "PUBLIC", + "type": "string" + }, + "logo": { + "defaultValue": "", + "type": "string" + }, + "cover_image": { + "defaultValue": "https://static.ghost.org/v5.0.0/images/publication-cover.jpg", + "type": "string" + }, + "icon": { + "defaultValue": "", + "type": "string" + }, + "accent_color": { + "defaultValue": "#FF1A75", + "flags": "PUBLIC", + "validations": { + "isEmpty": false + }, + "type": "string" + }, + "locale": { + "defaultValue": "en", + "validations": { + "isEmpty": false + }, + "type": "string" + }, + "timezone": { + "defaultValue": "Etc/UTC", + "validations": { + "isTimezone": true, + "isEmpty": false + }, + "type": "string" + }, + "codeinjection_head": { + "defaultValue": "", + "type": "string" + }, + "codeinjection_foot": { + "defaultValue": "", + "type": "string" + }, + "facebook": { + "defaultValue": "ghost", + "type": "string" + }, + "twitter": { + "defaultValue": "@ghost", + "type": "string" + }, + "navigation": { + "defaultValue": "[{\"label\":\"Home\",\"url\":\"/\"},{\"label\":\"About\",\"url\":\"/about/\"}]", + "type": "array" + }, + "secondary_navigation": { + "defaultValue": "[{\"label\":\"Sign up\",\"url\":\"#/portal/\"}]", + "type": "array" + }, + "meta_title": { + "defaultValue": null, + "validations": { + "isLength": { + "max": 300 + } + }, + "type": "string" + }, + "meta_description": { + "defaultValue": null, + "validations": { + "isLength": { + "max": 500 + } + }, + "type": "string" + }, + "og_image": { + "defaultValue": null, + "validations": { + "isLength": { + "max": 2000 + } + }, + "type": "string" + }, + "og_title": { + "defaultValue": null, + "validations": { + "isLength": { + "max": 300 + } + }, + "type": "string" + }, + "og_description": { + "defaultValue": null, + "validations": { + "isLength": { + "max": 300 + } + }, + "type": "string" + }, + "twitter_image": { + "defaultValue": null, + "validations": { + "isLength": { + "max": 2000 + } + }, + "type": "string" + }, + "twitter_title": { + "defaultValue": null, + "validations": { + "isLength": { + "max": 300 + } + }, + "type": "string" + }, + "twitter_description": { + "defaultValue": null, + "validations": { + "isLength": { + "max": 300 + } + }, + "type": "string" + } + }, + "theme": { + "active_theme": { + "defaultValue": "casper", + "flags": "RO", + "type": "string" + } + }, + "private": { + "is_private": { + "defaultValue": "false", + "validations": { + "isIn": [["true", "false"]] + }, + "type": "boolean" + }, + "password": { + "defaultValue": "", + "type": "string" + }, + "public_hash": { + "defaultValue": null, + "type": "string" + } + }, + "members": { + "default_content_visibility": { + "defaultValue": "public", + "type": "string" + }, + "default_content_visibility_tiers": { + "defaultValue": "[]", + "type": "array" + }, + "members_signup_access": { + "defaultValue": "all", + "validations": { + "isEmpty": false, + "isIn": [["all", "invite", "none"]] + }, + "type": "string" + }, + "members_support_address": { + "defaultValue": "noreply", + "flags": "PUBLIC,RO", + "type": "string" + }, + "stripe_secret_key": { + "defaultValue": null, + "type": "string" + }, + "stripe_publishable_key": { + "defaultValue": null, + "type": "string" + }, + "stripe_plans": { + "defaultValue": "[]", + "type": "array" + }, + "stripe_connect_publishable_key": { + "defaultValue": null, + "type": "string" + }, + "stripe_connect_secret_key": { + "defaultValue": null, + "type": "string" + }, + "stripe_connect_livemode": { + "defaultValue": null, + "type": "boolean" + }, + "stripe_connect_display_name": { + "defaultValue": null, + "type": "string" + }, + "stripe_connect_account_id": { + "defaultValue": null, + "type": "string" + }, + "members_monthly_price_id": { + "defaultValue": null, + "type": "string" + }, + "members_yearly_price_id": { + "defaultValue": null, + "type": "string" + }, + "members_track_sources": { + "defaultValue": "true", + "validations": { + "isEmpty": false, + "isIn": [["true", "false"]] + }, + "type": "boolean" + } + }, + "portal": { + "portal_name": { + "defaultValue": "true", + "validations": { + "isEmpty": false, + "isIn": [["true", "false"]] + }, + "type": "boolean" + }, + "portal_button": { + "defaultValue": "true", + "validations": { + "isEmpty": false, + "isIn": [["true", "false"]] + }, + "type": "boolean" + }, + "portal_plans": { + "defaultValue": "[\"free\"]", + "type": "array" + }, + "portal_products": { + "defaultValue": "[]", + "type": "array" + }, + "portal_button_style": { + "defaultValue": "icon-and-text", + "validations": { + "isEmpty": false, + "isIn": [["text-only", "icon-and-text", "icon-only"]] + }, + "type": "string" + }, + "portal_button_icon": { + "defaultValue": null, + "type": "string" + }, + "portal_button_signup_text": { + "defaultValue": "Subscribe", + "type": "string" + } + }, + "email": { + "mailgun_domain": { + "defaultValue": null, + "type": "string" + }, + "mailgun_api_key": { + "defaultValue": null, + "type": "string" + }, + "mailgun_base_url": { + "defaultValue": null, + "type": "string" + }, + "email_track_opens": { + "defaultValue": "true", + "validations": { + "isEmpty": false, + "isIn": [["true", "false"]] + }, + "type": "boolean" + }, + "email_track_clicks": { + "defaultValue": "true", + "validations": { + "isEmpty": false, + "isIn": [["true", "false"]] + }, + "type": "boolean" + }, + "email_verification_required": { + "defaultValue": "false", + "validations": { + "isEmpty": false, + "isIn": [["true", "false"]] + }, + "type": "boolean", + "flags": "RO" + } + }, + "amp": { + "amp": { + "defaultValue": "false", + "validations": { + "isIn": [["true", "false"]] + }, + "type": "boolean" + }, + "amp_gtag_id": { + "defaultValue": null, + "type": "string" + } + }, + "firstpromoter": { + "firstpromoter": { + "defaultValue": "false", + "validations": { + "isIn": [ + [ + "true", + "false" + ] + ] + }, + "type": "boolean" + }, + "firstpromoter_id": { + "defaultValue": null, + "type": "string" + } + }, + "labs": { + "labs": { + "defaultValue": "{}", + "type": "object" + } + }, + "slack": { + "slack_url": { + "defaultValue": "", + "type": "string" + }, + "slack_username": { + "defaultValue": "Ghost", + "type": "string" + } + }, + "unsplash": { + "unsplash": { + "defaultValue": "true", + "validations": { + "isEmpty": false, + "isIn": [["true", "false"]] + }, + "type": "boolean" + } + }, + "views": { + "shared_views": { + "defaultValue": "[]", + "type": "array" + } + }, + "editor": { + "editor_default_email_recipients": { + "defaultValue": "visibility", + "type": "string", + "validations": { + "isEmpty": false, + "isIn": [[ + "disabled", + "visibility", + "filter" + ]] + } + }, + "editor_default_email_recipients_filter": { + "defaultValue": "all", + "type": "string" + } + }, + "comments": { + "comments_enabled": { + "type": "string", + "defaultValue": "off", + "validations": { + "isEmpty": false, + "isIn": [[ + "off", + "all", + "paid" + ]] + } + } + } +}