diff --git a/.changeset/silver-jobs-peel.md b/.changeset/silver-jobs-peel.md new file mode 100644 index 0000000000..cfd3ab0b92 --- /dev/null +++ b/.changeset/silver-jobs-peel.md @@ -0,0 +1,5 @@ +--- +'@sap-ux/preview-middleware': patch +--- + +fix: remove hard coded usage of webapp folder diff --git a/packages/preview-middleware/README.md b/packages/preview-middleware/README.md index c59ea76b3c..5209de8f0b 100644 --- a/packages/preview-middleware/README.md +++ b/packages/preview-middleware/README.md @@ -188,7 +188,7 @@ return flp.router ## [Migration](#migration) -If you have no custom modifications in the local Fiori Launchpad sandbox files (`webapp/test/flpSandbox.html` or `webapp/test/flpSandboxMockserver.html`), the conversion is finished. +If you have no custom modifications in the local Fiori Launchpad sandbox files (`/test/flpSandbox.html` or `/test/flpSandboxMockserver.html`), the conversion is finished. If you have custom modifications in the local Fiori Launchpad sandbox files, you need to migrate them into a custom .js or .ts file (depending on your setup) and integrate this file as a custom `init` script into the configuration options of the middleware. diff --git a/packages/preview-middleware/src/base/flp.ts b/packages/preview-middleware/src/base/flp.ts index bd952d87e2..914f1c46b9 100644 --- a/packages/preview-middleware/src/base/flp.ts +++ b/packages/preview-middleware/src/base/flp.ts @@ -2,15 +2,16 @@ import type { ReaderCollection } from '@ui5/fs'; import { create as createStorage } from 'mem-fs'; import { create } from 'mem-fs-editor'; import type { Editor as MemFsEditor } from 'mem-fs-editor'; +import { readFileSync } from 'fs'; import { render } from 'ejs'; import type http from 'http'; -import type { Request, RequestHandler, Response, Router, NextFunction } from 'express'; -import { readFileSync } from 'fs'; -import { dirname, join, posix } from 'path'; +import type { Request, Response, Router, NextFunction } from 'express'; import { Router as createRouter, static as serveStatic, json } from 'express'; +import type connect from 'connect'; +import { dirname, join, posix } from 'path'; import type { Logger, ToolsLogger } from '@sap-ux/logger'; import type { MiddlewareUtils } from '@ui5/server'; -import type { Manifest } from '@sap-ux/project-access'; +import { getWebappPath, type Manifest } from '@sap-ux/project-access'; import { AdpPreview, type AdpPreviewConfig, @@ -20,10 +21,9 @@ import { } from '@sap-ux/adp-tooling'; import { isAppStudio, exposePort } from '@sap-ux/btp-utils'; import { FeatureToggleAccess } from '@sap-ux/feature-toggle'; - import { deleteChange, readChanges, writeChange } from './flex'; import { generateImportList, mergeTestConfigDefaults } from './test'; -import type { Editor, FlpConfig, MiddlewareConfig, RtaConfig, TestConfig } from '../types'; +import type { Editor, FlpConfig, InternalTestConfig, MiddlewareConfig, RtaConfig, TestConfig } from '../types'; import { getFlpConfigWithDefaults, createFlpTemplateConfig, @@ -33,7 +33,6 @@ import { addApp, getAppName } from './config'; -import type connect from 'connect'; const DEFAULT_LIVERELOAD_PORT = 35729; @@ -68,6 +67,7 @@ export class FlpSandbox { public readonly rta?: RtaConfig; public readonly test?: TestConfig[]; public readonly router: EnhancedRouter; + private fs: MemFsEditor | undefined; /** * Constructor setting defaults and keeping reference to workspace resources. @@ -238,6 +238,72 @@ export class FlpSandbox { } } + /** + * Handler for the GET requests to the runtime adaptation editor in developer mode. + * + * @param res the response + * @param rta runtime adaptation configuration + * @param previewUrl the url of the preview + * @private + */ + private async editorGetHandlerDeveloperMode(res: Response, rta: RtaConfig, previewUrl: string): Promise { + const scenario = rta.options?.scenario; + let templatePreviewUrl = `${previewUrl}?sap-ui-xx-viewCache=false&fiori-tools-rta-mode=forAdaptation&sap-ui-rta-skip-flex-validation=true&sap-ui-xx-condense-changes=true#${this.config.intent.object}-${this.config.intent.action}`; + if (scenario === 'ADAPTATION_PROJECT') { + templatePreviewUrl = templatePreviewUrl.replace('?', `?sap-ui-layer=${rta.layer}&`); + } + const template = readFileSync(join(__dirname, '../../templates/flp/editor.html'), 'utf-8'); + const features = FeatureToggleAccess.getAllFeatureToggles(); + const envPort = process.env.FIORI_TOOLS_LIVERELOAD_PORT; + let livereloadPort: number = envPort ? parseInt(envPort, 10) : DEFAULT_LIVERELOAD_PORT; + livereloadPort = isNaN(livereloadPort) ? DEFAULT_LIVERELOAD_PORT : livereloadPort; + const envLivereloadUrl = isAppStudio() ? await exposePort(livereloadPort) : undefined; + const html = render(template, { + previewUrl: templatePreviewUrl, + telemetry: rta.options?.telemetry ?? false, + appName: rta.options?.appName, + scenario, + livereloadPort, + livereloadUrl: envLivereloadUrl, + features: JSON.stringify(features) + }); + this.sendResponse(res, 'text/html', 200, html); + } + + /** + * Handler for the GET requests to the runtime adaptation editor. + * + * @param req the request + * @param res the response + * @param rta runtime adaptation configuration + * @param previewUrl the url of the preview + * @param editor editor configuration + * @private + */ + private async editorGetHandler( + req: Request, + res: Response, + rta: RtaConfig, + previewUrl: string, + editor: Editor + ): Promise { + if (!req.query['fiori-tools-rta-mode']) { + // Redirect to the same URL but add the necessary parameter + const params = structuredClone(req.query); + params['sap-ui-xx-viewCache'] = 'false'; + params['fiori-tools-rta-mode'] = 'true'; + params['sap-ui-rta-skip-flex-validation'] = 'true'; + params['sap-ui-xx-condense-changes'] = 'true'; + res.redirect(302, `${previewUrl}?${new URLSearchParams(params)}`); + return; + } + const html = (await this.generateSandboxForEditor(req, rta, editor)).replace( + '', + `\n` + ); + this.sendResponse(res, 'text/html', 200, html); + } + /** * Add additional routes for configured editors. * @@ -251,27 +317,7 @@ export class FlpSandbox { previewUrl = `${previewUrl}.inner.html`; editor.pluginScript ??= 'open/ux/preview/client/cpe/init'; this.router.get(editor.path, async (_req: Request, res: Response) => { - const scenario = rta.options?.scenario; - let templatePreviewUrl = `${previewUrl}?sap-ui-xx-viewCache=false&fiori-tools-rta-mode=forAdaptation&sap-ui-rta-skip-flex-validation=true&sap-ui-xx-condense-changes=true#${this.config.intent.object}-${this.config.intent.action}`; - if (scenario === 'ADAPTATION_PROJECT') { - templatePreviewUrl = templatePreviewUrl.replace('?', `?sap-ui-layer=${rta.layer}&`); - } - const template = readFileSync(join(__dirname, '../../templates/flp/editor.html'), 'utf-8'); - const features = FeatureToggleAccess.getAllFeatureToggles(); - const envPort = process.env.FIORI_TOOLS_LIVERELOAD_PORT; - let livereloadPort: number = envPort ? parseInt(envPort, 10) : DEFAULT_LIVERELOAD_PORT; - livereloadPort = isNaN(livereloadPort) ? DEFAULT_LIVERELOAD_PORT : livereloadPort; - const envLivereloadUrl = isAppStudio() ? await exposePort(livereloadPort) : undefined; - const html = render(template, { - previewUrl: templatePreviewUrl, - telemetry: rta.options?.telemetry ?? false, - appName: rta.options?.appName, - scenario, - livereloadPort, - livereloadUrl: envLivereloadUrl, - features: JSON.stringify(features) - }); - this.sendResponse(res, 'text/html', 200, html); + await this.editorGetHandlerDeveloperMode(res, rta, previewUrl); }); let path = dirname(editor.path); if (!path.endsWith('/')) { @@ -281,25 +327,53 @@ export class FlpSandbox { } this.router.get(previewUrl, async (req: Request, res: Response) => { - if (!req.query['fiori-tools-rta-mode']) { - // Redirect to the same URL but add the necessary parameter - const params = structuredClone(req.query); - params['sap-ui-xx-viewCache'] = 'false'; - params['fiori-tools-rta-mode'] = 'true'; - params['sap-ui-rta-skip-flex-validation'] = 'true'; - params['sap-ui-xx-condense-changes'] = 'true'; - res.redirect(302, `${previewUrl}?${new URLSearchParams(params)}`); - return; - } - const html = (await this.generateSandboxForEditor(req, rta, editor)).replace( - '', - `\n` - ); - this.sendResponse(res, 'text/html', 200, html); + await this.editorGetHandler(req, res, rta, previewUrl, editor); }); } } + /** + * Handler for the GET requests to the FLP. + * + * @param req the request + * @param res the response + * @param next the next function + * @private + */ + private async flpGetHandler( + req: EnhancedRequest | connect.IncomingMessage, + res: Response | http.ServerResponse, + next: NextFunction + ): Promise { + // connect API (karma test runner) has no request query property + if ('query' in req && 'redirect' in res && !req.query['sap-ui-xx-viewCache']) { + // Redirect to the same URL but add the necessary parameter + const params = structuredClone(req.query); + params['sap-ui-xx-viewCache'] = 'false'; + res.redirect(302, `${this.config.path}?${new URLSearchParams(params)}`); + return; + } + await this.setApplicationDependencies(); + // inform the user if a html file exists on the filesystem + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const file = await this.project.byPath(this.config.path); + if (file) { + this.logger.info(`HTML file returned at ${this.config.path} is loaded from the file system.`); + next(); + } else { + const ui5Version = await this.getUi5Version( + //use protocol from request header referer as fallback for connect API (karma test runner) + 'protocol' in req + ? req.protocol + : req.headers.referer?.substring(0, req.headers.referer.indexOf(':')) ?? 'http', + req.headers.host, + 'ui5-patched-router' in req ? req['ui5-patched-router']?.baseUrl : undefined + ); + const html = render(this.getSandboxTemplate(ui5Version.major), this.templateConfig); + this.sendResponse(res, 'text/html', 200, html); + } + } + /** * Add routes for html and scripts required for a local FLP. */ @@ -308,39 +382,16 @@ export class FlpSandbox { this.router.use(PREVIEW_URL.client.path, serveStatic(PREVIEW_URL.client.local)); // add route for the sandbox html - this.router.get(this.config.path, (async ( - req: EnhancedRequest | connect.IncomingMessage, - res: Response | http.ServerResponse, - next: NextFunction - ) => { - // connect API (karma test runner) has no request query property - if ('query' in req && 'redirect' in res && !req.query['sap-ui-xx-viewCache']) { - // Redirect to the same URL but add the necessary parameter - const params = structuredClone(req.query); - params['sap-ui-xx-viewCache'] = 'false'; - res.redirect(302, `${this.config.path}?${new URLSearchParams(params)}`); - return; - } - await this.setApplicationDependencies(); - // inform the user if a html file exists on the filesystem - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const file = await this.project.byPath(this.config.path); - if (file) { - this.logger.info(`HTML file returned at ${this.config.path} is loaded from the file system.`); - next(); - } else { - const ui5Version = await this.getUi5Version( - //use protocol from request header referer as fallback for connect API (karma test runner) - 'protocol' in req - ? req.protocol - : req.headers.referer?.substring(0, req.headers.referer.indexOf(':')) ?? 'http', - req.headers.host, - 'ui5-patched-router' in req ? req['ui5-patched-router']?.baseUrl : undefined - ); - const html = render(this.getSandboxTemplate(ui5Version.major), this.templateConfig); - this.sendResponse(res, 'text/html', 200, html); + this.router.get( + this.config.path, + async ( + req: EnhancedRequest | connect.IncomingMessage, + res: Response | http.ServerResponse, + next: NextFunction + ) => { + await this.flpGetHandler(req, res, next); } - }) as RequestHandler); + ); } /** @@ -428,10 +479,10 @@ export class FlpSandbox { for (const app of this.config.apps) { let manifest: Manifest | undefined; if (app.local) { - manifest = JSON.parse(readFileSync(join(app.local, 'webapp/manifest.json'), 'utf-8')) as - | Manifest - | undefined; - this.router.use(app.target, serveStatic(join(app.local, 'webapp'))); + this.fs = this.fs ?? create(createStorage()); + const webappPath = await getWebappPath(app.local, this.fs); + manifest = JSON.parse(readFileSync(join(webappPath, 'manifest.json'), 'utf-8')) as Manifest | undefined; + this.router.use(app.target, serveStatic(webappPath)); this.logger.info(`Serving additional application at ${app.target} from ${app.local}`); } else if (app.componentId) { manifest = { @@ -453,58 +504,140 @@ export class FlpSandbox { } /** - * Create required routes for flex. + * Handler for flex changes GET requests. + * + * @param res the response + * @private */ - private createFlexHandler(): void { - const fs = create(createStorage()); - const api = `${PREVIEW_URL.api}/changes`; - this.router.use(api, json()); - this.router.get(api, (async (_req: Request, res: Response) => { - const changes = await readChanges(this.project, this.logger); + private async flexGetHandler(res: Response): Promise { + const changes = await readChanges(this.project, this.logger); + if (this.onChangeRequest) { + this.fs = this.fs ?? create(createStorage()); + for (const change of Object.values(changes)) { + await this.onChangeRequest('read', change, this.fs, this.logger); + } + } + this.sendResponse(res, 'application/json', 200, JSON.stringify(changes)); + } + + /** + * Handler for flex changes POST requests. + * + * @param req the request + * @param res the response + * @private + */ + private async flexPostHandler(req: Request, res: Response): Promise { + this.fs = this.fs ?? create(createStorage()); + try { + const change = req.body as CommonChangeProperties; if (this.onChangeRequest) { - for (const change of Object.values(changes)) { - await this.onChangeRequest('read', change, fs, this.logger); - } + await this.onChangeRequest('write', change, this.fs, this.logger); } - this.sendResponse(res, 'application/json', 200, JSON.stringify(changes)); - }) as RequestHandler); - this.router.post(api, (async (req: Request, res: Response) => { - try { - const change = req.body as CommonChangeProperties; - if (this.onChangeRequest) { - await this.onChangeRequest('write', change, fs, this.logger); - } - const { success, message } = writeChange( - change, - this.utils.getProject().getSourcePath(), - fs, - this.logger - ); - if (success) { - fs.commit(() => this.sendResponse(res, 'text/plain', 200, message ?? '')); - } else { - this.sendResponse(res, 'text/plain', 400, 'INVALID_DATA'); - } - } catch (error) { - this.sendResponse(res, 'text/plain', 500, error.message); + const { success, message } = writeChange( + change, + this.utils.getProject().getSourcePath(), + this.fs, + this.logger + ); + if (success) { + this.fs.commit(() => this.sendResponse(res, 'text/plain', 200, message ?? '')); + } else { + this.sendResponse(res, 'text/plain', 400, 'INVALID_DATA'); } - }) as RequestHandler); - this.router.delete(api, (async (req: Request, res: Response) => { - try { - const { success, message } = deleteChange( - req.body, - this.utils.getProject().getSourcePath(), - this.logger - ); - if (success) { - this.sendResponse(res, 'text/plain', 200, message ?? ''); - } else { - this.sendResponse(res, 'text/plain', 400, 'INVALID_DATA'); - } - } catch (error) { - this.sendResponse(res, 'text/plain', 500, error.message); + } catch (error) { + this.sendResponse(res, 'text/plain', 500, error.message); + } + } + + /** + * Handler for flex changes DELETE requests. + * + * @param req the request + * @param res the response + * @private + */ + private async flexDeleteHandler(req: Request, res: Response): Promise { + try { + const { success, message } = deleteChange(req.body, this.utils.getProject().getSourcePath(), this.logger); + if (success) { + this.sendResponse(res, 'text/plain', 200, message ?? ''); + } else { + this.sendResponse(res, 'text/plain', 400, 'INVALID_DATA'); } - }) as RequestHandler); + } catch (error) { + this.sendResponse(res, 'text/plain', 500, error.message); + } + } + + /** + * Create required routes for flex. + */ + private createFlexHandler(): void { + const api = `${PREVIEW_URL.api}/changes`; + this.router.use(api, json()); + this.router.get(api, async (_req: Request, res: Response) => { + await this.flexGetHandler(res); + }); + this.router.post(api, async (req: Request, res: Response) => { + await this.flexPostHandler(req, res); + }); + this.router.delete(api, async (req: Request, res: Response) => { + await this.flexDeleteHandler(req, res); + }); + } + + /** + * Handler for the GET requests to the HTML of the test suite. + * + * @param res the response + * @param testsuite the test suite template + * @param config the test configuration + * @private + */ + private async testSuiteHtmlGetHandler( + res: Response | http.ServerResponse, + testsuite: string, + config: TestConfig + ): Promise { + this.logger.debug(`Serving test route: ${config.path}`); + const templateConfig = { + basePath: this.templateConfig.basePath, + initPath: config.init + }; + const html = render(testsuite, templateConfig); + this.sendResponse(res, 'text/html', 200, html); + } + + /** + * Handler for the GET requests to the JS of the test suite. + * + * @param res the response + * @param next the next function + * @param config the test configuration + * @param initTemplate the test runner template + * @param testPaths the paths to the test files + * @private + */ + private async testSuiteJsGetHandler( + res: Response | http.ServerResponse, + next: NextFunction, + config: InternalTestConfig, + initTemplate: string, + testPaths: string[] + ): Promise { + const files = await this.project.byGlob(config.init.replace('.js', '.[jt]s')); + if (files?.length > 0) { + this.logger.warn(`Script returned at ${config.path} is loaded from the file system.`); + next(); + } else { + this.logger.debug(`Serving test route: ${config.init}`); + const templateConfig = { + testPaths: testPaths + }; + const js = render(initTemplate, templateConfig); + this.sendResponse(res, 'application/javascript', 200, js); + } } /** @@ -524,22 +657,14 @@ export class FlpSandbox { return; } const testsuite = readFileSync(join(__dirname, '../../templates/test/testsuite.qunit.html'), 'utf-8'); - const initTemplate = readFileSync(join(__dirname, '../../templates/test/testsuite.qunit.js'), 'utf-8'); const config = mergeTestConfigDefaults(testsuiteConfig); this.logger.debug(`Add route for ${config.path}`); - this.router.get(config.path, (async ( - _req: EnhancedRequest | connect.IncomingMessage, - res: Response | http.ServerResponse, - _next: NextFunction - ) => { - this.logger.debug(`Serving test route: ${config.path}`); - const templateConfig = { - basePath: this.templateConfig.basePath, - initPath: config.init - }; - const html = render(testsuite, templateConfig); - this.sendResponse(res, 'text/html', 200, html); - }) as RequestHandler); + this.router.get( + config.path, + async (_req: EnhancedRequest | connect.IncomingMessage, res: Response | http.ServerResponse) => { + await this.testSuiteHtmlGetHandler(res, testsuite, config); + } + ); if (testsuiteConfig.init !== undefined) { this.logger.debug( @@ -557,25 +682,18 @@ export class FlpSandbox { testPaths.push(posix.relative(posix.dirname(config.path), mergedConfig.path)); } + const initTemplate = readFileSync(join(__dirname, '../../templates/test/testsuite.qunit.js'), 'utf-8'); this.logger.debug(`Add route for ${config.init}`); - this.router.get(config.init, (async ( - _req: EnhancedRequest | connect.IncomingMessage, - res: Response | http.ServerResponse, - next: NextFunction - ) => { - const files = await this.project.byGlob(config.init.replace('.js', '.[jt]s')); - if (files?.length > 0) { - this.logger.warn(`Script returned at ${config.path} is loaded from the file system.`); - next(); - } else { - this.logger.debug(`Serving test route: ${config.init}`); - const templateConfig = { - testPaths: testPaths - }; - const js = render(initTemplate, templateConfig); - this.sendResponse(res, 'application/javascript', 200, js); + this.router.get( + config.init, + async ( + _req: EnhancedRequest | connect.IncomingMessage, + res: Response | http.ServerResponse, + next: NextFunction + ) => { + await this.testSuiteJsGetHandler(res, next, config, initTemplate, testPaths); } - }) as RequestHandler); + ); } /** @@ -596,6 +714,66 @@ export class FlpSandbox { res.end(); } + /** + * Handler for the GET requests to the HTML of the test runner. + * + * @param res the response + * @param next the next function + * @param config test configuration + * @param htmlTemplate the test runner template + * @param id application id from manifest + */ + private async testRunnerHtmlGetHandler( + res: Response | http.ServerResponse, + next: NextFunction, + config: InternalTestConfig, + htmlTemplate: string, + id: string + ): Promise { + this.logger.debug(`Serving test route: ${config.path}`); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const file = await this.project.byPath(config.path); + if (file) { + this.logger.warn(`HTML file returned at ${config.path} is loaded from the file system.`); + next(); + } else { + const templateConfig = createTestTemplateConfig(config, id, this.templateConfig.ui5.theme); + const html = render(htmlTemplate, templateConfig); + this.sendResponse(res, 'text/html', 200, html); + } + } + + /** + * Handler for the GET requests to the JS of the test runner. + * + * @param res the response + * @param next the next function + * @param config test configuration + * @param initTemplate the test runner template + * @param ns namespace for the test files + * @private + */ + private async testRunnerJsGetHandler( + res: Response | http.ServerResponse, + next: NextFunction, + config: InternalTestConfig, + initTemplate: string, + ns: string + ): Promise { + this.logger.debug(`Serving test init script: ${config.init}`); + + const files = await this.project.byGlob(config.init.replace('.js', '.[jt]s')); + if (files?.length > 0) { + this.logger.warn(`Script returned at ${config.path} is loaded from the file system.`); + next(); + } else { + const testFiles = await this.project.byGlob(config.pattern); + const templateConfig = { tests: generateImportList(ns, testFiles) }; + const js = render(initTemplate, templateConfig); + this.sendResponse(res, 'application/javascript', 200, js); + } + } + /** * Add routes for html and scripts required for a local test FLP. * @@ -605,52 +783,37 @@ export class FlpSandbox { private addTestRoutes(configs: TestConfig[], id: string): void { const ns = id.replace(/\./g, '/'); const htmlTemplate = readFileSync(join(__dirname, '../../templates/test/qunit.html'), 'utf-8'); - const initTemplate = readFileSync(join(__dirname, '../../templates/test/qunit.js'), 'utf-8'); for (const testConfig of configs) { const config = mergeTestConfigDefaults(testConfig); this.logger.debug(`Add route for ${config.path}`); // add route for the *.qunit.html - this.router.get(config.path, (async ( - _req: EnhancedRequest | connect.IncomingMessage, - res: Response | http.ServerResponse, - next: NextFunction - ) => { - this.logger.debug(`Serving test route: ${config.path}`); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const file = await this.project.byPath(config.path); - if (file) { - this.logger.warn(`HTML file returned at ${config.path} is loaded from the file system.`); - next(); - } else { - const templateConfig = createTestTemplateConfig(config, id, this.templateConfig.ui5.theme); - const html = render(htmlTemplate, templateConfig); - this.sendResponse(res, 'text/html', 200, html); + this.router.get( + config.path, + async ( + _req: EnhancedRequest | connect.IncomingMessage, + res: Response | http.ServerResponse, + next: NextFunction + ) => { + await this.testRunnerHtmlGetHandler(res, next, config, htmlTemplate, id); } - }) as RequestHandler); + ); if (testConfig.init !== undefined) { this.logger.debug(`Skip serving test init script in favor of provided script: ${testConfig.init}`); continue; } // add route for the init file + const initTemplate = readFileSync(join(__dirname, '../../templates/test/qunit.js'), 'utf-8'); this.logger.debug(`Add route for ${config.init}`); - this.router.get(config.init, (async ( - _req: EnhancedRequest | connect.IncomingMessage, - res: Response | http.ServerResponse, - next: NextFunction - ) => { - this.logger.debug(`Serving test init script: ${config.init}`); - - const files = await this.project.byGlob(config.init.replace('.js', '.[jt]s')); - if (files?.length > 0) { - this.logger.warn(`Script returned at ${config.path} is loaded from the file system.`); - next(); - } else { - const testFiles = await this.project.byGlob(config.pattern); - const templateConfig = { tests: generateImportList(ns, testFiles) }; - const js = render(initTemplate, templateConfig); - this.sendResponse(res, 'application/javascript', 200, js); + this.router.get( + config.init, + async ( + _req: EnhancedRequest | connect.IncomingMessage, + res: Response | http.ServerResponse, + next: NextFunction + ) => { + await this.testRunnerJsGetHandler(res, next, config, initTemplate, ns); } - }) as RequestHandler); + ); } } } @@ -659,7 +822,7 @@ export class FlpSandbox { * Creates an attribute string that can be added to an HTML element. * * @param attributes map with attributes and their values - * @param indent indentation thats inserted before each attribute + * @param indent indentation that's inserted before each attribute * @param prefix value that should be added at the start of to all attribute names * @returns attribute string */ @@ -718,7 +881,7 @@ export async function initAdp( const descriptor = adp.descriptor; const { name, manifest } = descriptor; await flp.init(manifest, name, adp.resources, adp); - flp.router.use(adp.descriptor.url, adp.proxy.bind(adp) as RequestHandler); + flp.router.use(adp.descriptor.url, adp.proxy.bind(adp)); flp.addOnChangeRequestHandler(adp.onChangeRequest.bind(adp)); flp.router.use(json()); adp.addApis(flp.router);