mirror of
https://github.com/mattermost/mattermost.git
synced 2026-08-26 21:27:40 -05:00
* add testcontainers to playwright-lib * include retry mechanism for transient failures * - Introduced a new command `testcontainers:up` in package.json to run Playwright tests with Testcontainers. - Created a standalone Playwright configuration file `playwright.testcontainers-up.config.ts` for managing Testcontainers. - Added a no-op test `testcontainers_up.spec.ts` to ensure the Testcontainers stack is up during the test run. - Implemented a global setup script `testcontainers_up_global_setup.ts` to start and stop the Testcontainers stack. - Updated dependencies in package.json, including adding `chalk` for logging. * fix package-lock * fix tsc and restore waiting for all migrations to complete --------- Co-authored-by: Mattermost Build <build@mattermost.com>
115 lines
3.7 KiB
JavaScript
115 lines
3.7 KiB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
/* eslint-disable no-console */
|
|
|
|
// A tiny file server that serves the contents of the `asset` directory for use by tests for things like link previews
|
|
// and Markdown images that need an external server which the MM server can contact.
|
|
//
|
|
// Run standalone with `npm run start:file-server-mock`, or fork it from a spec. When forked with an IPC channel,
|
|
// it posts a {type: 'listening', port} message once it is ready so the parent can learn the port (use PORT=0 to get
|
|
// an ephemeral one).
|
|
|
|
const {createServer} = require('http');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const PORT = Number(process.env.PORT) || 3011;
|
|
const ASSET_DIR = process.env.ASSET_DIR || path.join(__dirname, 'asset');
|
|
|
|
if (process.argv[2]) {
|
|
process.title = process.argv[2];
|
|
}
|
|
|
|
const CONTENT_TYPES = {
|
|
'.png': 'image/png',
|
|
'.jpg': 'image/jpeg',
|
|
'.jpeg': 'image/jpeg',
|
|
'.gif': 'image/gif',
|
|
'.svg': 'image/svg+xml',
|
|
'.txt': 'text/plain; charset=utf-8',
|
|
'.html': 'text/html; charset=utf-8',
|
|
'.json': 'application/json',
|
|
'.zip': 'application/zip',
|
|
};
|
|
|
|
function setCorsHeaders(res) {
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Range');
|
|
}
|
|
|
|
const server = createServer((req, res) => {
|
|
const method = req.method;
|
|
let requestPath;
|
|
try {
|
|
requestPath = decodeURIComponent(req.url.split('?')[0]);
|
|
} catch (err) {
|
|
console.log(`[mock-file-server] Invalid request URL ${req.url.split('?')[0]}:`, err);
|
|
res.writeHead(400);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
// Handle CORS preflight
|
|
if (method === 'OPTIONS') {
|
|
setCorsHeaders(res);
|
|
res.writeHead(204);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
console.log(`[mock-file-server] ${method} ${requestPath}`);
|
|
|
|
if (method !== 'GET' && method !== 'HEAD') {
|
|
res.writeHead(405);
|
|
res.end('Method not allowed');
|
|
return;
|
|
}
|
|
|
|
// Resolve the requested file within ASSET_DIR, rejecting anything that escapes it (path traversal).
|
|
const relativePath = requestPath.replace(/^\/+/, '');
|
|
const filePath = path.join(ASSET_DIR, relativePath);
|
|
if (filePath !== ASSET_DIR && !filePath.startsWith(ASSET_DIR + path.sep)) {
|
|
res.writeHead(403);
|
|
res.end('Forbidden');
|
|
return;
|
|
}
|
|
|
|
fs.stat(filePath, (err, stats) => {
|
|
if (err || !stats.isFile()) {
|
|
console.log(`[mock-file-server] 404: ${requestPath}`);
|
|
res.writeHead(404);
|
|
res.end('Not found');
|
|
return;
|
|
}
|
|
|
|
setCorsHeaders(res);
|
|
const ext = path.extname(filePath).toLowerCase();
|
|
res.setHeader('Content-Type', CONTENT_TYPES[ext] || 'application/octet-stream');
|
|
res.setHeader('Content-Length', stats.size);
|
|
|
|
if (method === 'HEAD') {
|
|
res.writeHead(200);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
res.writeHead(200);
|
|
fs.createReadStream(filePath).pipe(res);
|
|
});
|
|
});
|
|
|
|
// Bind to all interfaces, not just loopback, so the Mattermost server container can reach this
|
|
// over the Docker network in `testcontainers` mode.
|
|
server.listen(PORT, () => {
|
|
const address = server.address();
|
|
const actualPort = typeof address === 'object' && address ? address.port : PORT;
|
|
console.log(`File server serving ${ASSET_DIR} on port ${actualPort}!`);
|
|
|
|
// Let the parent process know the server is ready and which port it's on
|
|
if (process.send) {
|
|
process.send({type: 'listening', port: actualPort});
|
|
}
|
|
});
|