2023-10-13 14:42:39 +03:00
|
|
|
// 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();
|
|
|
|
|
2023-10-16 18:32:13 +03:00
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
*/
|
2023-10-13 14:42:39 +03:00
|
|
|
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);
|
|
|
|
},
|
|
|
|
|
2023-10-16 18:32:13 +03:00
|
|
|
sharedPage: [async ({browser}, use) => {
|
|
|
|
const page = await browser.newPage();
|
|
|
|
await use(page);
|
|
|
|
}, {scope: 'worker'}],
|
|
|
|
|
2023-10-13 14:42:39 +03:00
|
|
|
// 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}]
|
|
|
|
});
|