Ghost/ghost/core/test/e2e-browser/fixtures/ghost-test.js
Sam Lord ccbcba0969
Switched to a shared page for each playwright worker
refs: https://github.com/TryGhost/DevOps/issues/78

This speeds up the tests by another 30 seconds on my local machine, and
hopefully takes some time off in CI too
2023-10-16 15:32:13 +00:00

156 lines
6.2 KiB
JavaScript

// express-test.js
const base = require('@playwright/test');
const {promisify} = require('util');
const {spawn, exec} = require('child_process');
const {setupGhost, setupMailgun, enableLabs, setupStripe, getStripeAccountId, generateStripeIntegrationToken} = require('../utils/e2e-browser-utils');
const {allowStripe, mockMail} = require('../../utils/e2e-framework-mock-manager');
const MailgunClient = require('@tryghost/mailgun-client');
const sinon = require('sinon');
const ObjectID = require('bson-objectid').default;
const Stripe = require('stripe').Stripe;
const configUtils = require('../../utils/configUtils');
const startWebhookServer = (port) => {
const command = `stripe listen --forward-connect-to http://127.0.0.1:${port}/members/webhooks/stripe/ ${process.env.CI ? `--api-key ${process.env.STRIPE_SECRET_KEY}` : ''}`.trim();
const webhookServer = spawn(command.split(' ')[0], command.split(' ').slice(1));
// Adding event listeners here seems to prevent heisenbug where webhooks aren't received
webhookServer.stdout.on('data', () => {});
webhookServer.stderr.on('data', () => {});
return webhookServer;
};
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();
};
// Global promises for webhook secret / Stripe integration token
const webhookSecretPromise = getWebhookSecret();
/**
* @type {import('@playwright/test').TestType<
* import('@playwright/test').PlaywrightTestArgs &
* import('@playwright/test').PlaywrightTestOptions &
* import('@playwright/test').PlaywrightWorkerArgs &
* import('@playwright/test').PlaywrightWorkerOptions
* >}
* @property {import('@playwright/test').Page} sharedPage
*/
module.exports = base.test.extend({
baseURL: async ({port, baseURL}, use) => {
// Replace the port in baseURL with the one we got from the port fixture
const url = new URL(baseURL);
url.port = port.toString();
await use(url.toString());
},
storageState: async ({ghost}, use) => {
await use(ghost.state);
},
sharedPage: [async ({browser}, use) => {
const page = await browser.newPage();
await use(page);
}, {scope: 'worker'}],
// eslint-disable-next-line no-empty-pattern
port: [async ({}, use, workerInfo) => {
await use(2369 + workerInfo.parallelIndex);
}, {scope: 'worker'}],
ghost: [async ({browser, port}, use, workerInfo) => {
// Do not initialise database before this block
const currentDate = new Date();
const formattedDate = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(currentDate.getDate()).padStart(2, '0')}-${String(currentDate.getHours()).padStart(2, '0')}-${String(currentDate.getMinutes()).padStart(2, '0')}-${String(currentDate.getSeconds()).padStart(2, '0')}`;
process.env.database__connection__filename = `/tmp/ghost-playwright.${workerInfo.workerIndex}.${formattedDate}.db`;
configUtils.set('database:connection:filename', process.env.database__connection__filename);
configUtils.set('server:port', port);
configUtils.set('url', `http://127.0.0.1:${port}`);
const stripeAccountId = await getStripeAccountId();
const stripeIntegrationToken = await generateStripeIntegrationToken(stripeAccountId);
const WebhookManager = require('../../../../stripe/lib/WebhookManager');
const originalParseWebhook = WebhookManager.prototype.parseWebhook;
const sandbox = sinon.createSandbox();
sandbox.stub(WebhookManager.prototype, 'parseWebhook').callsFake(function (body, signature) {
const parsedBody = JSON.parse(body);
if (!('account' in parsedBody)) {
throw new Error('Webhook without account');
} else if (parsedBody.account !== stripeAccountId) {
throw new Error('Webhook for wrong account');
} else {
return originalParseWebhook.call(this, body, signature);
}
});
const StripeAPI = require('../../../../stripe/lib/StripeAPI');
const originalStripeConfigure = StripeAPI.prototype.configure;
sandbox.stub(StripeAPI.prototype, 'configure').callsFake(function (stripeConfig) {
originalStripeConfigure.call(this, stripeConfig);
if (stripeConfig) {
this._stripe = new Stripe(stripeConfig.secretKey, {
apiVersion: '2020-08-27',
stripeAccount: stripeAccountId
});
}
});
const stripeServer = startWebhookServer(port);
process.env.WEBHOOK_SECRET = await webhookSecretPromise;
sandbox.stub(MailgunClient.prototype, 'getInstance').returns({
// @ts-ignore
messages: {
create: async function () {
return {
id: `mailgun-mock-id-${ObjectID().toHexString()}`
};
}
}
});
mockMail();
const {startGhost} = require('../../utils/e2e-framework');
const server = await startGhost({
frontend: true,
server: true,
backend: true
});
// StartGhost automatically disables network, so we need to re-enable it for Stripe
allowStripe();
const page = await browser.newPage({
baseURL: `http://127.0.0.1:${port}/`,
storageState: undefined
});
await setupGhost(page);
await setupStripe(page, stripeIntegrationToken);
await setupMailgun(page);
await enableLabs(page);
const state = await page.context().storageState();
await page.close();
// Use the server in the tests.
try {
await use({
server,
state
});
} finally {
const {stopGhost} = require('../../utils/e2e-utils');
await stopGhost();
stripeServer.kill();
sandbox.restore();
}
}, {scope: 'worker', auto: true}]
});