From 6ba61f19f4a768e6fbe20f2dda412da4d0cc6f7e Mon Sep 17 00:00:00 2001 From: Miguel Serrano Date: Mon, 17 Jun 2024 13:05:06 +0200 Subject: [PATCH] Merge pull request #18893 from overleaf/msm-sp-e2e-native-run [CE/SP] Added `test-e2e-native` runner GitOrigin-RevId: a123b2a83a47566a091acc37ccef6c4895bc9949 --- server-ce/test/Makefile | 4 ++ server-ce/test/docker-compose.native.yml | 14 ++++++ server-ce/test/git-bridge.spec.ts | 13 ++++-- server-ce/test/helpers/hostAdminClient.ts | 12 +++-- server-ce/test/host-admin.js | 56 +++++++++++++---------- 5 files changed, 68 insertions(+), 31 deletions(-) create mode 100644 server-ce/test/docker-compose.native.yml diff --git a/server-ce/test/Makefile b/server-ce/test/Makefile index 905eb9fc1f..949640dfff 100644 --- a/server-ce/test/Makefile +++ b/server-ce/test/Makefile @@ -10,6 +10,10 @@ export TEX_LIVE_DOCKER_IMAGE ?= quay.io/sharelatex/texlive-full:2023.1 export ALL_TEX_LIVE_DOCKER_IMAGES ?= quay.io/sharelatex/texlive-full:2023.1,quay.io/sharelatex/texlive-full:2022.1 export IMAGE_TAG_PRO ?= quay.io/sharelatex/sharelatex-pro:latest +test-e2e-native: + docker compose -f docker-compose.yml -f docker-compose.native.yml up --build --no-log-prefix sharelatex host-admin -d + CYPRESS_ADMIN_CLIENT_URL='http://localhost:8081' CYPRESS_GIT_BRIDGE_PUBLIC_HOST='localhost' npm run cypress:open + test-e2e: docker compose up --build --no-log-prefix --exit-code-from=e2e e2e diff --git a/server-ce/test/docker-compose.native.yml b/server-ce/test/docker-compose.native.yml new file mode 100644 index 0000000000..d8e92f309d --- /dev/null +++ b/server-ce/test/docker-compose.native.yml @@ -0,0 +1,14 @@ +version: '2.2' +services: + sharelatex: + ports: + - "127.0.0.1:80:80" + environment: + OVERLEAF_SITE_URL: 'http://localhost' + + host-admin: + ports: + - "127.0.0.1:8081:80" + environment: + NATIVE_CYPRESS: 'true' + ACCESS_CONTROL_ALLOW_ORIGIN: 'http://localhost' diff --git a/server-ce/test/git-bridge.spec.ts b/server-ce/test/git-bridge.spec.ts index ce32a57913..e6a07e75d1 100644 --- a/server-ce/test/git-bridge.spec.ts +++ b/server-ce/test/git-bridge.spec.ts @@ -15,6 +15,9 @@ describe('git-bridge', function () { V1_HISTORY_URL: 'http://sharelatex:3100/api', } + const gitBridgePublicHost = + Cypress.env('GIT_BRIDGE_PUBLIC_HOST') || 'sharelatex' + describe('enabled in Server Pro', function () { startWith({ pro: true, @@ -74,7 +77,9 @@ describe('git-bridge', function () { cy.findByText('Git').click() cy.findByRole('dialog').within(() => { cy.get('@projectId').then(id => { - cy.get('code').contains(`git clone http://git@sharelatex/git/${id}`) + cy.get('code').contains( + `git clone http://git@${gitBridgePublicHost}/git/${id}` + ) }) cy.findByRole('button', { name: 'Generate token', @@ -88,7 +93,9 @@ describe('git-bridge', function () { cy.findByText('Git').click() cy.findByRole('dialog').within(() => { cy.get('@projectId').then(id => { - cy.get('code').contains(`git clone http://git@sharelatex/git/${id}`) + cy.get('code').contains( + `git clone http://git@${gitBridgePublicHost}/git/${id}` + ) }) cy.findByText('Generate token').should('not.exist') cy.findByText(/generate a new one in Account Settings/) @@ -109,7 +116,7 @@ describe('git-bridge', function () { cy.get('@projectId').then(projectId => { cy.findByRole('dialog').within(() => { cy.get('code').contains( - `git clone http://git@sharelatex/git/${projectId}` + `git clone http://git@${gitBridgePublicHost}/git/${projectId}` ) }) cy.findByRole('button', { diff --git a/server-ce/test/helpers/hostAdminClient.ts b/server-ce/test/helpers/hostAdminClient.ts index 393b55441c..9ed0088a83 100644 --- a/server-ce/test/helpers/hostAdminClient.ts +++ b/server-ce/test/helpers/hostAdminClient.ts @@ -1,5 +1,7 @@ +const hostAdminUrl = Cypress.env('ADMIN_CLIENT_URL') || 'http://host-admin' + export async function dockerCompose(cmd: string, ...args: string[]) { - return await fetchJSON(`http://host-admin/docker/compose/${cmd}`, { + return await fetchJSON(`${hostAdminUrl}/docker/compose/${cmd}`, { method: 'POST', body: JSON.stringify({ args, @@ -8,13 +10,13 @@ export async function dockerCompose(cmd: string, ...args: string[]) { } export async function mongoInit() { - return await fetchJSON('http://host-admin/mongo/init', { + return await fetchJSON(`${hostAdminUrl}/mongo/init`, { method: 'POST', }) } export async function resetData() { - return await fetchJSON('http://host-admin/reset/data', { + return await fetchJSON(`${hostAdminUrl}/reset/data`, { method: 'POST', }) } @@ -25,7 +27,7 @@ export async function reconfigure({ vars = {}, withDataDir = false, }) { - return await fetchJSON('http://host-admin/reconfigure', { + return await fetchJSON(`${hostAdminUrl}/reconfigure`, { method: 'POST', body: JSON.stringify({ pro, @@ -65,7 +67,7 @@ export async function runScript({ script: string args?: string[] }) { - return await fetchJSON('http://host-admin/run/script', { + return await fetchJSON(`${hostAdminUrl}/run/script`, { method: 'POST', body: JSON.stringify({ cwd, diff --git a/server-ce/test/host-admin.js b/server-ce/test/host-admin.js index a16423523c..03b7521d37 100644 --- a/server-ce/test/host-admin.js +++ b/server-ce/test/host-admin.js @@ -11,7 +11,9 @@ const { const YAML = require('js-yaml') const PATHS = { + DOCKER_COMPOSE_FILE: 'docker-compose.yml', DOCKER_COMPOSE_OVERRIDE: 'docker-compose.override.yml', + DOCKER_COMPOSE_NATIVE: 'docker-compose.native.yml', DATA_DIR: Path.join(__dirname, 'data'), SANDBOXED_COMPILES_HOST_DIR: Path.join(__dirname, 'data/compiles'), } @@ -44,6 +46,17 @@ function writeDockerComposeOverride(cfg) { fs.writeFileSync(PATHS.DOCKER_COMPOSE_OVERRIDE, YAML.dump(cfg)) } +function runDockerCompose(command, args, callback) { + const files = ['-f', PATHS.DOCKER_COMPOSE_FILE] + if (process.env.NATIVE_CYPRESS) { + files.push('-f', PATHS.DOCKER_COMPOSE_NATIVE) + } + if (fs.existsSync(PATHS.DOCKER_COMPOSE_OVERRIDE)) { + files.push('-f', PATHS.DOCKER_COMPOSE_OVERRIDE) + } + execFile('docker', ['compose', ...files, command, ...args], callback) +} + function purgeDataDir() { fs.rmSync(PATHS.DATA_DIR, { recursive: true, force: true }) } @@ -58,7 +71,9 @@ app.use((req, res, next) => { // Basic access logs console.log(req.method, req.url, req.body) // Add CORS headers - res.setHeader('Access-Control-Allow-Origin', 'http://sharelatex') + const accessControlAllowOrigin = + process.env.ACCESS_CONTROL_ALLOW_ORIGIN || 'http://sharelatex' + res.setHeader('Access-Control-Allow-Origin', accessControlAllowOrigin) res.setHeader('Access-Control-Allow-Headers', 'Content-Type') res.setHeader('Access-Control-Max-Age', '3600') next() @@ -79,11 +94,9 @@ app.post( (req, res) => { const { cwd, script, args } = req.body - execFile( - 'docker', + runDockerCompose( + 'exec', [ - 'compose', - 'exec', 'sharelatex', 'bash', '-c', @@ -204,24 +217,22 @@ app.post( if (['stop', 'down'].includes(cmd)) { mongoIsInitialized = false } - execFile('docker', ['compose', cmd, ...args], (error, stdout, stderr) => { + runDockerCompose(cmd, args, (error, stdout, stderr) => { res.json({ error, stdout, stderr }) }) } ) function mongoInit(callback) { - execFile( - 'docker', - ['compose', 'up', '--detach', '--wait', 'mongo'], + runDockerCompose( + 'up', + ['--detach', '--wait', 'mongo'], (error, stdout, stderr) => { if (error) return callback(error, stdout, stderr) - execFile( - 'docker', + runDockerCompose( + 'exec', [ - 'compose', - 'exec', 'mongo', 'mongo', '--eval', @@ -268,10 +279,9 @@ app.post( const doMongoInit = mongoIsInitialized ? cb => cb() : mongoInit doMongoInit((error, stdout, stderr) => { if (error) return res.json({ error, stdout, stderr }) - - execFile( - 'docker', - ['compose', 'up', '--detach', '--wait', 'sharelatex'], + runDockerCompose( + 'up', + ['--detach', '--wait', 'sharelatex'], (error, stdout, stderr) => { res.json({ error, stdout, stderr }) } @@ -281,9 +291,9 @@ app.post( ) app.post('/reset/data', (req, res) => { - execFile( - 'docker', - ['compose', 'stop', '--timeout=0', 'sharelatex'], + runDockerCompose( + 'stop', + ['--timeout=0', 'sharelatex'], (error, stdout, stderr) => { if (error) return res.json({ error, stdout, stderr }) @@ -294,9 +304,9 @@ app.post('/reset/data', (req, res) => { } mongoIsInitialized = false - execFile( - 'docker', - ['compose', 'down', '--timeout=0', '--volumes', 'mongo', 'redis'], + runDockerCompose( + 'down', + ['--timeout=0', '--volumes', 'mongo', 'redis'], (error, stdout, stderr) => { res.json({ error, stdout, stderr }) }