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
This commit is contained in:
Naz 2022-12-06 17:30:07 +07:00 committed by Daniel Lockyer
parent a302e8289f
commit 6ef5e6a7e0
9 changed files with 585 additions and 27 deletions

View File

@ -11,7 +11,7 @@ module.exports = class RecordTest extends Command {
}
permittedEnvironments() {
return ['development', 'testing'];
return ['testing-browser'];
}
async handle(argv) {

View File

@ -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;
}

View File

@ -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"
}
}

View File

@ -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",

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,8 @@
/**
* Teardown the environment
*/
const teardown = async (playwrightConfig) => {
// @NOTE: local environment should probably drop the db state here
};
module.exports = teardown;

View File

@ -385,7 +385,8 @@ module.exports = {
getGhostAPIAgent,
getAgentsWithFrontend
},
// @NOTE: startGhost only exposed for playwright tests
startGhost,
// Mocks and Stubs
mockManager,

View File

@ -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"
]]
}
}
}
}