From ee654c3350d2c437d2db3422a615b5c2b2596c34 Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Thu, 3 Oct 2024 14:25:18 +0200 Subject: [PATCH 01/13] test(SSR e2e): re-enable test "should receive response with status 500 if HTTP error occurred when calling other than cms/pages API URL" --- projects/ssr-tests/src/ssr-testing.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/ssr-tests/src/ssr-testing.spec.ts b/projects/ssr-tests/src/ssr-testing.spec.ts index b891d6bc25c..3f696fedc02 100644 --- a/projects/ssr-tests/src/ssr-testing.spec.ts +++ b/projects/ssr-tests/src/ssr-testing.spec.ts @@ -66,11 +66,11 @@ describe('SSR E2E', () => { expect(response.statusCode).toEqual(404); }); - it.skip('should receive response with status 500 if HTTP error occurred when calling other than cms/pages API URL', async () => { + it('should receive response with status 500 if HTTP error occurred when calling other than cms/pages API URL', async () => { backendProxy = await ProxyUtils.startBackendProxyServer({ target: BACKEND_BASE_URL, responseInterceptor: ({ res, req, body }) => { - if (req.url?.includes('cms/pages')) { + if (req.url?.includes('cms/components')) { res.statusCode = 404; } res.end(body); From e6e1e3ec6c40e266548199f4cdb184978215af87 Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Thu, 3 Oct 2024 15:25:23 +0200 Subject: [PATCH 02/13] empty commit From 489ce2f14263847bcc7c58d40e3b5ecee13a9d29 Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Thu, 3 Oct 2024 16:29:30 +0200 Subject: [PATCH 03/13] chore: console.warn instead of throwing Error while validating JSONs in logs. And provide more context about the problem with parsing --- projects/ssr-tests/src/utils/log.utils.ts | 38 +++++++++++++++++------ 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/projects/ssr-tests/src/utils/log.utils.ts b/projects/ssr-tests/src/utils/log.utils.ts index 654acff728d..8ab81465b7b 100644 --- a/projects/ssr-tests/src/utils/log.utils.ts +++ b/projects/ssr-tests/src/utils/log.utils.ts @@ -27,25 +27,43 @@ export function clearSsrLogFile(): void { /** * Validates that all lines starting with `{` are valid JSON objects. - * Otherwise it throws an error. + * Otherwise it warns. * * Note: multi-line JSONs (printed by SSR in dev mode) cannot be parsed by `JSON.parse`. * That's why we need to to run SSR in prod mode to get single line JSON logs. */ -function validateJsonsInLogs(logs: string[]): void { - logs.forEach((text) => { - if (text.charAt(0) === '{') { +function validateJsonsInLogs(rawLogs: string[]): void { + for (const logLine of rawLogs) { + if (logLine.charAt(0) === '{') { try { - JSON.parse(text); - } catch (error) { - throw new Error( - `Encountered in SSR Logs a line starting with \`{\` that could not be parsed as JSON. + JSON.parse(logLine); + } catch (_e) { + const surroundingLogs = rawLogs.slice( + Math.max(rawLogs.indexOf(logLine) - 2, 0), + rawLogs.indexOf(logLine) + 3 + ); + console.warn( + ` + Encountered in SSR Logs a line starting with \`{\` that could not be parsed as JSON. Perhaps its a multi-line JSON log from SSR dev mode. - Please make sure to build Spartacus SSR in prod mode - to get single line JSONs that can be parsed in tests.` + Please make sure to build Spartacus SSR in prod mode prior to running SSR Tests + to get single line JSONs that can be parsed in tests. + + For specific context, see 5 raw log lines below (2 previous lines, current line, 2 next lines): +\`\`\` +${surroundingLogs.join('\n')} +\`\`\` + + For general context, see full raw logs below: +\`\`\` +${rawLogs.join('\n')} +\`\`\` +` ); + break; } } - }); + } } /** From c7d8e7f9c63cb0622da802608a1163e1c948608a Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Thu, 3 Oct 2024 17:36:47 +0200 Subject: [PATCH 04/13] chore: fix SonarQube complain --- projects/ssr-tests/src/utils/log.utils.ts | 36 +++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/projects/ssr-tests/src/utils/log.utils.ts b/projects/ssr-tests/src/utils/log.utils.ts index 8ab81465b7b..d630e45b755 100644 --- a/projects/ssr-tests/src/utils/log.utils.ts +++ b/projects/ssr-tests/src/utils/log.utils.ts @@ -38,10 +38,13 @@ function validateJsonsInLogs(rawLogs: string[]): void { try { JSON.parse(logLine); } catch (_e) { - const surroundingLogs = rawLogs.slice( - Math.max(rawLogs.indexOf(logLine) - 2, 0), - rawLogs.indexOf(logLine) + 3 - ); + const surroundingLinesRadius = 2; + const surroundingLines = getSurroundingLines({ + allLines: rawLogs, + line: logLine, + radius: surroundingLinesRadius, + }); + console.warn( ` Encountered in SSR Logs a line starting with \`{\` that could not be parsed as JSON. @@ -51,7 +54,7 @@ function validateJsonsInLogs(rawLogs: string[]): void { For specific context, see 5 raw log lines below (2 previous lines, current line, 2 next lines): \`\`\` -${surroundingLogs.join('\n')} +${surroundingLines.join('\n')} \`\`\` For general context, see full raw logs below: @@ -66,6 +69,29 @@ ${rawLogs.join('\n')} } } +/** + * Returns the given line with surrounding lines. + * e.g. if the radius is 2, the function will return the given line + * and the 2 lines before and 2 lines after it. In other words, it returns an array of 5 lines. + */ +function getSurroundingLines({ + allLines, + line, + radius, +}: { + allLines: string[]; + line: string; + radius: number; +}): string[] { + const logLineIndex = allLines.indexOf(line); + const surroundingStartIndex = Math.max(0, logLineIndex - radius); + const surroundingEndIndex = Math.min( + allLines.length, + logLineIndex + radius + 1 + ); + return allLines.slice(surroundingStartIndex, surroundingEndIndex); +} + /** * Returns raw logs as an array of strings. * From 5d61fb06c496aefaa77074fdeb29a35e8eb89d8e Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Thu, 3 Oct 2024 17:40:00 +0200 Subject: [PATCH 05/13] refactor: reuse `surroundingLinesRadius` variable --- projects/ssr-tests/src/utils/log.utils.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/projects/ssr-tests/src/utils/log.utils.ts b/projects/ssr-tests/src/utils/log.utils.ts index d630e45b755..419106e6ce8 100644 --- a/projects/ssr-tests/src/utils/log.utils.ts +++ b/projects/ssr-tests/src/utils/log.utils.ts @@ -52,14 +52,9 @@ function validateJsonsInLogs(rawLogs: string[]): void { Please make sure to build Spartacus SSR in prod mode prior to running SSR Tests to get single line JSONs that can be parsed in tests. - For specific context, see 5 raw log lines below (2 previous lines, current line, 2 next lines): + For specific context, see ${surroundingLinesRadius * 2 + 1} raw log lines below (previous ${surroundingLinesRadius} lines, current line, next ${surroundingLinesRadius} lines): \`\`\` ${surroundingLines.join('\n')} -\`\`\` - - For general context, see full raw logs below: -\`\`\` -${rawLogs.join('\n')} \`\`\` ` ); From f58ec4fd35c603f509bb9d365eb101b47c4a7849 Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Fri, 4 Oct 2024 11:40:55 +0200 Subject: [PATCH 06/13] chore: validate the build ouptut file main.js for using prod mode and proxy backend before running tests. Previously we were checking the app's behavior in the runtime of tests, checking its symptoms of running in dev mode. --- projects/ssr-tests/jest.config.js | 1 + projects/ssr-tests/src/utils/log.utils.ts | 63 ----------------- projects/ssr-tests/validate-ssr-build.ts | 84 +++++++++++++++++++++++ 3 files changed, 85 insertions(+), 63 deletions(-) create mode 100644 projects/ssr-tests/validate-ssr-build.ts diff --git a/projects/ssr-tests/jest.config.js b/projects/ssr-tests/jest.config.js index f0aea45398b..9216d61df84 100644 --- a/projects/ssr-tests/jest.config.js +++ b/projects/ssr-tests/jest.config.js @@ -23,4 +23,5 @@ module.exports = { }, }, testEnvironment: './src/environments/custom-test-environment.ts', + globalSetup: './validate-ssr-build.ts', }; diff --git a/projects/ssr-tests/src/utils/log.utils.ts b/projects/ssr-tests/src/utils/log.utils.ts index 419106e6ce8..507385ed565 100644 --- a/projects/ssr-tests/src/utils/log.utils.ts +++ b/projects/ssr-tests/src/utils/log.utils.ts @@ -25,68 +25,6 @@ export function clearSsrLogFile(): void { fs.writeFileSync(SSR_LOG_PATH, ''); } -/** - * Validates that all lines starting with `{` are valid JSON objects. - * Otherwise it warns. - * - * Note: multi-line JSONs (printed by SSR in dev mode) cannot be parsed by `JSON.parse`. - * That's why we need to to run SSR in prod mode to get single line JSON logs. - */ -function validateJsonsInLogs(rawLogs: string[]): void { - for (const logLine of rawLogs) { - if (logLine.charAt(0) === '{') { - try { - JSON.parse(logLine); - } catch (_e) { - const surroundingLinesRadius = 2; - const surroundingLines = getSurroundingLines({ - allLines: rawLogs, - line: logLine, - radius: surroundingLinesRadius, - }); - - console.warn( - ` - Encountered in SSR Logs a line starting with \`{\` that could not be parsed as JSON. - Perhaps its a multi-line JSON log from SSR dev mode. - Please make sure to build Spartacus SSR in prod mode prior to running SSR Tests - to get single line JSONs that can be parsed in tests. - - For specific context, see ${surroundingLinesRadius * 2 + 1} raw log lines below (previous ${surroundingLinesRadius} lines, current line, next ${surroundingLinesRadius} lines): -\`\`\` -${surroundingLines.join('\n')} -\`\`\` -` - ); - break; - } - } - } -} - -/** - * Returns the given line with surrounding lines. - * e.g. if the radius is 2, the function will return the given line - * and the 2 lines before and 2 lines after it. In other words, it returns an array of 5 lines. - */ -function getSurroundingLines({ - allLines, - line, - radius, -}: { - allLines: string[]; - line: string; - radius: number; -}): string[] { - const logLineIndex = allLines.indexOf(line); - const surroundingStartIndex = Math.max(0, logLineIndex - radius); - const surroundingEndIndex = Math.min( - allLines.length, - logLineIndex + radius + 1 - ); - return allLines.slice(surroundingStartIndex, surroundingEndIndex); -} - /** * Returns raw logs as an array of strings. * @@ -98,7 +36,6 @@ function getSurroundingLines({ export function getRawLogs(): string[] { const data = fs.readFileSync(SSR_LOG_PATH).toString(); const logs = data.toString().split('\n'); - validateJsonsInLogs(logs); return logs; } diff --git a/projects/ssr-tests/validate-ssr-build.ts b/projects/ssr-tests/validate-ssr-build.ts new file mode 100644 index 00000000000..7f311aa5a68 --- /dev/null +++ b/projects/ssr-tests/validate-ssr-build.ts @@ -0,0 +1,84 @@ +/** + * Verify that the SSR app under tests meets the following criteria: + * 1. The app us built in prod mode. + * + * Why it's needed: + * Some tests require parsing single-line JSON log entries appearing in the SSR logs. + * In an app built in prod mode, the JSON objects are printed single-line that can be easily parsed in tests. + * In an app built in dev mode, the JSON objects are printed multi-line (to be more human-readable), + * but cannot be parsed in tests so it makes some tests to fail. + * + * 2. The app has configured a base OCC url being a local backend proxy, instead of a real backend. + * + * Why it's needed: + * Some tests require mocking the behavior of the backend using a local backend http proxy. + * If the app is not calling a local backend proxy as a base OCC url, some tests will fail. + * + * + * In this file we'll validate the build of the app before running the tests. + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +/** + * String that always appears in the `main.js` file of the SSR app when built with a local backend proxy. + */ +const USING_PROXY_BACKEND_MARKER = `CX_BASE_URL:"http://localhost:9002"`; + +/** + * String that always appears in the `main.js` file of the SSR app when built in dev mode. + */ +const USING_DEV_MODE_MARKER = `ngDevMode`; + +/** + * Path to the `main.js` file of the built SSR app. + */ +const SSR_APP_PATH = path.join( + __dirname, + '../../dist/storefrontapp-server/main.js' +); + +/** + * The command to build the SSR app with a local backend proxy. + */ +const BUILD_COMMAND = 'npm run build && npm run build:ssr:local-http-backend'; + +/** + * Advice to the user on how to build the SSR app with a local backend proxy. + */ +const BUILD_COMMAND_ADVICE = `Please build the SSR app with the following command: + +${colorToYellow(BUILD_COMMAND)} +`; + +export default async function validateSsrBuild() { + if (!fs.existsSync(SSR_APP_PATH)) { + throw new Error( + ` +SSR app not found at the expected path '${SSR_APP_PATH}'. +${BUILD_COMMAND_ADVICE}` + ); + } + + const fileContents = fs.readFileSync(SSR_APP_PATH, 'utf8'); + if (!fileContents.includes(USING_PROXY_BACKEND_MARKER)) { + throw new Error( + ` +SSR app is not using a local backend proxy as a base OCC url. +${BUILD_COMMAND_ADVICE}` + ); + } + + if (fileContents.includes(USING_DEV_MODE_MARKER)) { + throw new Error( + ` +SSR app is not using prod mode. +${BUILD_COMMAND_ADVICE}` + ); + } +} + +function colorToYellow(text: string) { + return `\x1b[33m${text}\x1b[0m`; +} From a1c0502db91cf4238f478ce49f41bdd552cfef6c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 4 Oct 2024 09:41:26 +0000 Subject: [PATCH 07/13] Add license header --- projects/ssr-tests/validate-ssr-build.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/projects/ssr-tests/validate-ssr-build.ts b/projects/ssr-tests/validate-ssr-build.ts index 7f311aa5a68..b661de80d15 100644 --- a/projects/ssr-tests/validate-ssr-build.ts +++ b/projects/ssr-tests/validate-ssr-build.ts @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + /** * Verify that the SSR app under tests meets the following criteria: * 1. The app us built in prod mode. From a1d902e64767ade821a30712f7d295b11f23b40e Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Fri, 4 Oct 2024 11:47:30 +0200 Subject: [PATCH 08/13] refactor: highlight to yellow the whole advice --- projects/ssr-tests/validate-ssr-build.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/projects/ssr-tests/validate-ssr-build.ts b/projects/ssr-tests/validate-ssr-build.ts index 7f311aa5a68..8becc2ddd18 100644 --- a/projects/ssr-tests/validate-ssr-build.ts +++ b/projects/ssr-tests/validate-ssr-build.ts @@ -42,15 +42,16 @@ const SSR_APP_PATH = path.join( /** * The command to build the SSR app with a local backend proxy. */ -const BUILD_COMMAND = 'npm run build && npm run build:ssr:local-http-backend'; +const BUILD_COMMAND = '> npm run build && npm run build:ssr:local-http-backend'; /** * Advice to the user on how to build the SSR app with a local backend proxy. */ -const BUILD_COMMAND_ADVICE = `Please build the SSR app with the following command: +const BUILD_COMMAND_ADVICE = + colorToYellow(`Please build the SSR app with the following command: -${colorToYellow(BUILD_COMMAND)} -`; +${BUILD_COMMAND} +`); export default async function validateSsrBuild() { if (!fs.existsSync(SSR_APP_PATH)) { From 2769dc9b4f63016b2d14329351bfc7e4b56cd529 Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Fri, 4 Oct 2024 11:52:48 +0200 Subject: [PATCH 09/13] doc: fix jsdoc --- projects/ssr-tests/src/utils/log.utils.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/projects/ssr-tests/src/utils/log.utils.ts b/projects/ssr-tests/src/utils/log.utils.ts index 507385ed565..e722b5c6c69 100644 --- a/projects/ssr-tests/src/utils/log.utils.ts +++ b/projects/ssr-tests/src/utils/log.utils.ts @@ -29,9 +29,6 @@ export function clearSsrLogFile(): void { * Returns raw logs as an array of strings. * * Note: Non-JSON log entries are also included in the returned array. - * - * It also validates whether each line starting with `{` is a valid JSON object. - * Otherwise it throws an error. */ export function getRawLogs(): string[] { const data = fs.readFileSync(SSR_LOG_PATH).toString(); From 5605f5f0ce388c43d0c42ec65c53186b9c71d4b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Fri, 4 Oct 2024 11:54:05 +0200 Subject: [PATCH 10/13] revert un-skipping one test. as it's not in the scope of this PR --- projects/ssr-tests/src/ssr-testing.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/ssr-tests/src/ssr-testing.spec.ts b/projects/ssr-tests/src/ssr-testing.spec.ts index 3f696fedc02..b891d6bc25c 100644 --- a/projects/ssr-tests/src/ssr-testing.spec.ts +++ b/projects/ssr-tests/src/ssr-testing.spec.ts @@ -66,11 +66,11 @@ describe('SSR E2E', () => { expect(response.statusCode).toEqual(404); }); - it('should receive response with status 500 if HTTP error occurred when calling other than cms/pages API URL', async () => { + it.skip('should receive response with status 500 if HTTP error occurred when calling other than cms/pages API URL', async () => { backendProxy = await ProxyUtils.startBackendProxyServer({ target: BACKEND_BASE_URL, responseInterceptor: ({ res, req, body }) => { - if (req.url?.includes('cms/components')) { + if (req.url?.includes('cms/pages')) { res.statusCode = 404; } res.end(body); From 602eb13fb432f0675522170b6f155a31161964e1 Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Fri, 4 Oct 2024 12:05:15 +0200 Subject: [PATCH 11/13] refactor: remove variable BUILD_COMMAND --- projects/ssr-tests/validate-ssr-build.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/projects/ssr-tests/validate-ssr-build.ts b/projects/ssr-tests/validate-ssr-build.ts index 720f425ab08..9308cadd0f6 100644 --- a/projects/ssr-tests/validate-ssr-build.ts +++ b/projects/ssr-tests/validate-ssr-build.ts @@ -46,18 +46,12 @@ const SSR_APP_PATH = path.join( ); /** - * The command to build the SSR app with a local backend proxy. - */ -const BUILD_COMMAND = '> npm run build && npm run build:ssr:local-http-backend'; - -/** - * Advice to the user on how to build the SSR app with a local backend proxy. + * Advice to the user on how to build the SSR app in prod mode to use a local backend proxy. */ const BUILD_COMMAND_ADVICE = colorToYellow(`Please build the SSR app with the following command: -${BUILD_COMMAND} -`); +> npm run build && npm run build:ssr:local-http-backend`); export default async function validateSsrBuild() { if (!fs.existsSync(SSR_APP_PATH)) { From 6158e4baf6664dd7e5e00269718533394f001202 Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Fri, 4 Oct 2024 12:14:24 +0200 Subject: [PATCH 12/13] refactor: improve colors of message --- projects/ssr-tests/validate-ssr-build.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/ssr-tests/validate-ssr-build.ts b/projects/ssr-tests/validate-ssr-build.ts index 9308cadd0f6..7430b00c2eb 100644 --- a/projects/ssr-tests/validate-ssr-build.ts +++ b/projects/ssr-tests/validate-ssr-build.ts @@ -49,9 +49,8 @@ const SSR_APP_PATH = path.join( * Advice to the user on how to build the SSR app in prod mode to use a local backend proxy. */ const BUILD_COMMAND_ADVICE = - colorToYellow(`Please build the SSR app with the following command: - -> npm run build && npm run build:ssr:local-http-backend`); + colorToYellow('Please build the SSR app with the following command\n') + + colorToYellow('> npm run build && npm run build:ssr:local-http-backend\n'); export default async function validateSsrBuild() { if (!fs.existsSync(SSR_APP_PATH)) { From a865c49d8c8e467c45f2054c9574075c15e63cfc Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Fri, 4 Oct 2024 12:57:18 +0200 Subject: [PATCH 13/13] test(SSR e2e): before running tests, check if some process listens on the port 4000. If yes, avoid running errors and give advice on how to kill that process --- projects/ssr-tests/validate-ssr-build.ts | 56 ++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/projects/ssr-tests/validate-ssr-build.ts b/projects/ssr-tests/validate-ssr-build.ts index 7430b00c2eb..5ff1f6503d6 100644 --- a/projects/ssr-tests/validate-ssr-build.ts +++ b/projects/ssr-tests/validate-ssr-build.ts @@ -6,6 +6,7 @@ /** * Verify that the SSR app under tests meets the following criteria: + * * 1. The app us built in prod mode. * * Why it's needed: @@ -20,13 +21,24 @@ * Some tests require mocking the behavior of the backend using a local backend http proxy. * If the app is not calling a local backend proxy as a base OCC url, some tests will fail. * + * 3. No other process is already listening on the HTTP port used by the SSR server. + * + * Why it's needed: + * In tests we start a new SSR server process listening on a specific port. + * If anything is listening on that port, the tests will fail. * * In this file we'll validate the build of the app before running the tests. */ import * as fs from 'fs'; +import * as http from 'http'; import * as path from 'path'; +/** + * The port the SSR server will listen on. + */ +const SSR_PORT = 4000; + /** * String that always appears in the `main.js` file of the SSR app when built with a local backend proxy. */ @@ -52,6 +64,22 @@ const BUILD_COMMAND_ADVICE = colorToYellow('Please build the SSR app with the following command\n') + colorToYellow('> npm run build && npm run build:ssr:local-http-backend\n'); +/** + * Advice to the user on how to kill the process listening on the port. + */ +export const KILL_PORT_ADVICE = + colorToYellow( + `Please kill the process listening on port ${SSR_PORT} to free the port for the SSR server.\n` + ) + + // include 2 commands that will work both on Mac and Linux for port 4000 + colorToYellow('a) on Mac/Linux run:\n') + + colorToYellow(` > kill -9 $(lsof -t -i :${SSR_PORT})\n`) + + colorToYellow( + 'b) on Windows run 2 commands (to find PID and then to kill it): \n' + ) + + colorToYellow(` > netstat -ano | findstr :${SSR_PORT}\n`) + + colorToYellow(` > taskkill /f /pid \n`); + export default async function validateSsrBuild() { if (!fs.existsSync(SSR_APP_PATH)) { throw new Error( @@ -77,8 +105,36 @@ SSR app is not using prod mode. ${BUILD_COMMAND_ADVICE}` ); } + + if (!(await isHttpPortFree(SSR_PORT))) { + throw new Error( + ` +Port ${SSR_PORT} is already in use. +${KILL_PORT_ADVICE}` + ); + } } function colorToYellow(text: string) { return `\x1b[33m${text}\x1b[0m`; } + +async function isHttpPortFree(port: number): Promise { + return new Promise((resolve) => { + const server = http.createServer(); + + server.listen(port, () => { + server.close(() => { + resolve(true); + }); + }); + + server.once('error', (error: NodeJS.ErrnoException) => { + if (error.code === 'EADDRINUSE') { + resolve(false); + } else { + resolve(true); + } + }); + }); +}