2021-12-07 12:51:08 +03:00
|
|
|
// Set of common function that should be main building blocks for e2e tests.
|
|
|
|
// The e2e tests usually consist of following building blocks:
|
|
|
|
// - request agent
|
|
|
|
// - state builder
|
2021-12-07 19:21:48 +03:00
|
|
|
// - output state checker (in case we don't get jest snapshots working)
|
2021-12-07 12:51:08 +03:00
|
|
|
//
|
|
|
|
// The request agetnt is responsible for making HTTP-like requests to an application (express app in case of Ghost).
|
|
|
|
// Note there's no actual need to make an HTTP request to an actual server, bypassing HTTP and hooking into the application
|
|
|
|
// directly is enough and reduces dependence on blocking a port (allows to run tests in parallel).
|
|
|
|
//
|
|
|
|
// The state builder is responsible for building the state of the application. Usually it's done by using pre-defined fixtures.
|
|
|
|
// Can include building a DB state, file system state (themes, config files), building configuration state (config files) etc.
|
|
|
|
//
|
|
|
|
// The output state checker is responsible for checking the response from the app after performing a request.
|
2021-12-07 19:21:48 +03:00
|
|
|
const _ = require('lodash');
|
|
|
|
const {sequence} = require('@tryghost/promise');
|
2022-02-08 19:36:08 +03:00
|
|
|
const {any, stringMatching} = require('@tryghost/jest-snapshot');
|
2021-12-09 11:48:01 +03:00
|
|
|
const fs = require('fs-extra');
|
|
|
|
const path = require('path');
|
|
|
|
const os = require('os');
|
|
|
|
const uuid = require('uuid');
|
2021-12-09 14:10:06 +03:00
|
|
|
|
2022-02-08 20:13:30 +03:00
|
|
|
const fixtureUtils = require('./fixture-utils');
|
2021-12-09 11:48:01 +03:00
|
|
|
const redirectsUtils = require('./redirects');
|
|
|
|
const configUtils = require('./configUtils');
|
2022-02-08 19:52:38 +03:00
|
|
|
const mockManager = require('./e2e-framework-mock-manager');
|
2021-12-07 12:51:08 +03:00
|
|
|
|
|
|
|
const boot = require('../../core/boot');
|
2021-12-07 19:21:48 +03:00
|
|
|
const TestAgent = require('./test-agent');
|
|
|
|
const db = require('./db-utils');
|
2021-12-07 12:51:08 +03:00
|
|
|
|
2021-12-07 19:21:48 +03:00
|
|
|
const startGhost = async () => {
|
2021-12-09 11:48:01 +03:00
|
|
|
/**
|
|
|
|
* We never use the root content folder for testing!
|
|
|
|
* We use a tmp folder.
|
|
|
|
*/
|
|
|
|
const contentFolder = path.join(os.tmpdir(), uuid.v4(), 'ghost-test');
|
|
|
|
await prepareContentFolder({contentFolder});
|
|
|
|
|
|
|
|
// NOTE: need to pass this config to the server instance
|
|
|
|
configUtils.set('paths:contentPath', contentFolder);
|
|
|
|
|
2021-12-07 12:51:08 +03:00
|
|
|
const defaults = {
|
|
|
|
backend: true,
|
|
|
|
frontend: false,
|
|
|
|
server: false
|
|
|
|
};
|
|
|
|
|
2022-02-07 18:43:25 +03:00
|
|
|
// Ensure the DB state
|
|
|
|
await resetDb();
|
|
|
|
|
2021-12-07 12:51:08 +03:00
|
|
|
return boot(defaults);
|
|
|
|
};
|
|
|
|
|
2021-12-09 11:48:01 +03:00
|
|
|
/**
|
|
|
|
* Slightly simplified copy-paste from e2e-utils.
|
|
|
|
* @param {Object} options
|
|
|
|
*/
|
|
|
|
const prepareContentFolder = ({contentFolder, redirectsFile = true, routesFile = true}) => {
|
|
|
|
const contentFolderForTests = contentFolder;
|
|
|
|
|
|
|
|
fs.ensureDir(contentFolderForTests);
|
|
|
|
fs.ensureDir(path.join(contentFolderForTests, 'data'));
|
|
|
|
fs.ensureDir(path.join(contentFolderForTests, 'themes'));
|
|
|
|
fs.ensureDir(path.join(contentFolderForTests, 'images'));
|
|
|
|
fs.ensureDir(path.join(contentFolderForTests, 'logs'));
|
|
|
|
fs.ensureDir(path.join(contentFolderForTests, 'adapters'));
|
|
|
|
fs.ensureDir(path.join(contentFolderForTests, 'settings'));
|
|
|
|
|
|
|
|
// Copy all themes into the new test content folder. Default active theme is always casper.
|
|
|
|
// If you want to use a different theme, you have to set the active theme (e.g. stub)
|
|
|
|
fs.copySync(
|
|
|
|
path.join(__dirname, 'fixtures', 'themes'),
|
|
|
|
path.join(contentFolderForTests, 'themes')
|
|
|
|
);
|
|
|
|
|
|
|
|
if (redirectsFile) {
|
|
|
|
redirectsUtils.setupFile(contentFolderForTests, '.yaml');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (routesFile) {
|
|
|
|
fs.copySync(
|
|
|
|
path.join(__dirname, 'fixtures', 'settings', 'routes.yaml'),
|
|
|
|
path.join(contentFolderForTests, 'settings', 'routes.yaml')
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-12-07 19:21:48 +03:00
|
|
|
/**
|
|
|
|
* Database state builder. By default inserts an owner user into the database.
|
|
|
|
* @param {...any} [options]
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
|
|
|
const initFixtures = async (...options) => {
|
|
|
|
// No DB setup, but override the owner
|
|
|
|
options = _.merge({'owner:post': true}, _.transform(options, function (result, val) {
|
|
|
|
if (val) {
|
|
|
|
result[val] = true;
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
|
2022-02-08 20:13:30 +03:00
|
|
|
const fixtureOps = fixtureUtils.getFixtureOps(options);
|
2021-12-07 19:21:48 +03:00
|
|
|
|
|
|
|
return sequence(fixtureOps);
|
|
|
|
};
|
|
|
|
|
|
|
|
const getFixture = (type, index = 0) => {
|
2022-02-08 20:13:30 +03:00
|
|
|
return fixtureUtils.DataGenerator.forKnex[type][index];
|
2021-12-07 19:21:48 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
const resetDb = async () => {
|
2022-02-07 18:43:25 +03:00
|
|
|
await db.reset();
|
2021-12-07 19:21:48 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2022-02-08 17:39:24 +03:00
|
|
|
* Creates a TestAgent which is a drop-in substitution for supertest
|
|
|
|
* It is automatically hooked up to the Admin API so you can make requests to e.g.
|
|
|
|
* agent.get('/posts/') without having to worry about URL paths
|
|
|
|
*
|
|
|
|
* @returns {TestAgent} agent
|
2021-12-07 19:21:48 +03:00
|
|
|
*/
|
2022-02-08 17:39:24 +03:00
|
|
|
const getAdminAPIAgent = async () => {
|
2022-02-06 19:18:12 +03:00
|
|
|
try {
|
|
|
|
const app = await startGhost();
|
|
|
|
const originURL = configUtils.config.get('url');
|
|
|
|
|
|
|
|
return new TestAgent(app, {
|
2022-02-08 17:39:24 +03:00
|
|
|
apiURL: '/ghost/api/canary/admin/',
|
2022-02-06 19:18:12 +03:00
|
|
|
originURL
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
throw new Error('Unable to create test agent');
|
|
|
|
}
|
2021-12-07 12:51:08 +03:00
|
|
|
};
|
|
|
|
|
2022-02-07 19:02:04 +03:00
|
|
|
module.exports = {
|
|
|
|
// request agent
|
|
|
|
agentProvider: {
|
2022-02-08 17:39:24 +03:00
|
|
|
getAdminAPIAgent
|
2022-02-07 19:02:04 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
// Mocks and Stubs
|
2022-02-08 19:52:38 +03:00
|
|
|
mockManager,
|
2022-02-07 19:02:04 +03:00
|
|
|
|
|
|
|
// DB State Manipulation
|
|
|
|
fixtureManager: {
|
|
|
|
get: getFixture,
|
2022-02-08 20:13:30 +03:00
|
|
|
getCurrentOwnerUser: fixtureUtils.getCurrentOwnerUser,
|
2022-02-07 19:02:04 +03:00
|
|
|
init: initFixtures,
|
|
|
|
reset: resetDb
|
2022-02-08 17:33:19 +03:00
|
|
|
},
|
2022-02-08 19:36:08 +03:00
|
|
|
matchers: {
|
|
|
|
anyString: any(String),
|
2022-02-07 15:51:21 +03:00
|
|
|
anyArray: any(Array),
|
2022-02-08 19:36:08 +03:00
|
|
|
anyDate: stringMatching(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.000Z/),
|
|
|
|
anyEtag: stringMatching(/(?:W\/)?"(?:[ !#-\x7E\x80-\xFF]*|\r\n[\t ]|\\.)*"/),
|
|
|
|
anyObjectId: stringMatching(/[a-f0-9]{24}/),
|
|
|
|
anyErrorId: stringMatching(/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/),
|
|
|
|
anyUuid: stringMatching(/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/),
|
|
|
|
stringMatching
|
|
|
|
}
|
2022-02-07 19:02:04 +03:00
|
|
|
};
|