From ae4f6c693ab7308afb92fa0ca8034e0aa97801c2 Mon Sep 17 00:00:00 2001 From: johnjbarton Date: Tue, 26 Jan 2021 15:34:05 -0800 Subject: [PATCH] feat(bundler): add a bundler layer The input config supplies 'patterns' which FileList expands into Files. These files are preprocessed and then 'file-list-modified' is fired. The event offers a pair of file lists, {included, served}. The bundle layer maps this pair to a new pair where fewer files are included. By default the bundler copies the inputs to outputs. Fixes #3633 --- lib/middleware/karma.js | 12 ++-- lib/middleware/source_files.js | 7 +- lib/server.js | 12 ++-- lib/web-files.js | 45 ++++++++++++ lib/web-server.js | 9 +-- test/unit/middleware/karma.spec.js | 87 ++++++++++------------- test/unit/middleware/source_files.spec.js | 70 +++++++++--------- test/unit/web-server.spec.js | 9 +-- test/unit/web_files.spec.js | 69 ++++++++++++++++++ 9 files changed, 208 insertions(+), 112 deletions(-) create mode 100644 lib/web-files.js create mode 100644 test/unit/web_files.spec.js diff --git a/lib/middleware/karma.js b/lib/middleware/karma.js index 527a77f1a..ecf2dd9d2 100644 --- a/lib/middleware/karma.js +++ b/lib/middleware/karma.js @@ -59,7 +59,7 @@ function getXUACompatibleUrl (url) { } function createKarmaMiddleware ( - currentFiles, + currentWebFiles, serveStaticFile, serveFile, injector, @@ -132,7 +132,6 @@ function createKarmaMiddleware ( const isRequestingDebugFile = requestUrl === '/debug.html' const isRequestingClientContextFile = requestUrl === '/client_with_context.html' if (isRequestingContextFile || isRequestingDebugFile || isRequestingClientContextFile) { - const files = currentFiles.files let fileServer let requestedFileUrl log.debug('custom files', customContextFile, customDebugFile, customClientContextFile) @@ -157,7 +156,7 @@ function createKarmaMiddleware ( fileServer(requestedFileUrl, requestedRangeHeader, response, function (data) { common.setNoCacheHeaders(response) const scriptTags = [] - for (const file of files.included) { + for (const file of currentWebFiles.included) { let filePath = file.path const fileType = file.type || file.detectType() @@ -212,7 +211,7 @@ function createKarmaMiddleware ( } } - const mappings = data.includes('%MAPPINGS%') ? files.served.map((file) => { + const mappings = data.includes('%MAPPINGS%') ? currentWebFiles.served.map((file) => { const filePath = filePathToUrlPath(file.path, basePath, urlRoot, proxyPath) .replace(/\\/g, '\\\\') // Windows paths contain backslashes and generate bad IDs if not escaped .replace(/'/g, '\\\'') // Escape single quotes - double quotes should not be allowed! @@ -228,11 +227,10 @@ function createKarmaMiddleware ( .replace('\n%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url)) }) } else if (requestUrl === '/context.json') { - const files = currentFiles.files common.setNoCacheHeaders(response) response.writeHead(200) response.end(JSON.stringify({ - files: files.included.map((file) => filePathToUrlPath(file.path + '?' + file.sha, basePath, urlRoot, proxyPath)) + files: currentWebFiles.included.map((file) => filePathToUrlPath(file.path + '?' + file.sha, basePath, urlRoot, proxyPath)) })) } else { next() @@ -241,7 +239,7 @@ function createKarmaMiddleware ( } createKarmaMiddleware.$inject = [ - 'currentFiles', + 'currentWebFiles', 'serveStaticFile', 'serveFile', 'injector', diff --git a/lib/middleware/source_files.js b/lib/middleware/source_files.js index b6fcc9a6e..3181d795b 100644 --- a/lib/middleware/source_files.js +++ b/lib/middleware/source_files.js @@ -18,7 +18,7 @@ function composeUrl (url, basePath, urlRoot) { } // Source Files middleware is responsible for serving all the source files under the test. -function createSourceFilesMiddleware (currentFiles, serveFile, basePath, urlRoot) { +function createSourceFilesMiddleware (currentWebFiles, serveFile, basePath, urlRoot) { return function (request, response, next) { const requestedFilePath = composeUrl(request.url, basePath, urlRoot) // When a path contains HTML-encoded characters (e.g %2F used by Jenkins for branches with /) @@ -29,9 +29,8 @@ function createSourceFilesMiddleware (currentFiles, serveFile, basePath, urlRoot log.debug(`Requesting ${request.url}`) log.debug(`Fetching ${requestedFilePath}`) - const files = currentFiles.files // TODO(vojta): change served to be a map rather then an array - const file = findByPath(files.served, requestedFilePath) || findByPath(files.served, requestedFilePathUnescaped) + const file = findByPath(currentWebFiles.served, requestedFilePath) || findByPath(currentWebFiles.served, requestedFilePathUnescaped) const rangeHeader = request.headers.range if (file) { @@ -60,7 +59,7 @@ function createSourceFilesMiddleware (currentFiles, serveFile, basePath, urlRoot } createSourceFilesMiddleware.$inject = [ - 'currentFiles', 'serveFile', 'config.basePath', 'config.urlRoot' + 'currentWebFiles', 'serveFile', 'config.basePath', 'config.urlRoot' ] exports.create = createSourceFilesMiddleware diff --git a/lib/server.js b/lib/server.js index 19c055d51..3f4f186de 100644 --- a/lib/server.js +++ b/lib/server.js @@ -19,8 +19,9 @@ const plugin = require('./plugin') const createServeFile = require('./web-server').createServeFile const createServeStaticFile = require('./web-server').createServeStaticFile -const createCurrentFiles = require('./web-server').createCurrentFiles +const createFilesPromise = require('./web-server').createFilesPromise const createWebServer = require('./web-server').createWebServer +const createWebFiles = require('./web-files').createWebFiles const preprocessor = require('./preprocessor') const Launcher = require('./launcher').Launcher const FileList = require('./file-list') @@ -82,7 +83,10 @@ class Server extends KarmaEventEmitter { webServer: ['factory', createWebServer], serveFile: ['factory', createServeFile], serveStaticFile: ['factory', createServeStaticFile], - currentFiles: ['factory', createCurrentFiles], + // Obsolete, do not use + filesPromise: ['factory', createFilesPromise], + // The files to be included/served to browser for test + currentWebFiles: ['factory', createWebFiles], socketServer: ['factory', createSocketIoServer], executor: ['factory', Executor.factory], // TODO: Deprecated. Remove in the next major @@ -343,8 +347,8 @@ class Server extends KarmaEventEmitter { } if (config.autoWatch) { - this.on('file_list_modified', () => { - this.log.debug('List of files has changed, trying to execute') + this.on('web_files_modified', () => { + this.log.debug('List of included/served files has changed, trying to execute') if (config.restartOnFileChange) { socketServer.sockets.emit('stop') } diff --git a/lib/web-files.js b/lib/web-files.js new file mode 100644 index 000000000..9856ed7c5 --- /dev/null +++ b/lib/web-files.js @@ -0,0 +1,45 @@ +/** + Container class for the files included in context.html and served upon + request. Two instances fit into the files pipeline: + 1. The result from FileList.files, the list of preprocessed input files. + 2. The result of bundling #1 stored in currentWebFiles +*/ +class WebFiles { + constructor (included, served) { + this.included = [...included] + this.served = [...served] + } +} + +let currentWebFiles = new WebFiles([], []) + +function noOpBundler (webFiles) { + return new WebFiles(webFiles.included, webFiles.served) +} + +let bundler = noOpBundler + +function registerBundler (emitter) { + emitter.on('file_list_modified', (modifiedWebFiles) => { + // Any bundler is expected to return a WebFiles object. + // For sourcemap support, //# sourceUrl in the bundler result should + // point back to the input files. + currentWebFiles = Object.assign(currentWebFiles, bundler(modifiedWebFiles)) + emitter.emit('web_files_modified', currentWebFiles) + }) +} + +function createWebFiles (configBundler, instantiatePlugin, emitter) { + if (configBundler) { + bundler = instantiatePlugin('bundler', configBundler) + } + registerBundler(emitter) + return currentWebFiles +} + +createWebFiles.$inject = ['config.bundler', 'instantiatePlugin', 'emitter'] + +module.exports = { + createWebFiles, + WebFiles +} diff --git a/lib/web-server.js b/lib/web-server.js index a74d464d5..394066b7b 100644 --- a/lib/web-server.js +++ b/lib/web-server.js @@ -46,13 +46,6 @@ function createFilesPromise (emitter, fileList) { return Promise.resolve(files).then(...args) } } - -function createCurrentFiles (emitter, fileList) { - const currentFileList = { files: fileList.files } - emitter.on('file_list_modified', (modifiedFiles) => { - currentFileList.files = modifiedFiles - }) - return currentFileList } function createServeStaticFile (config) { @@ -121,5 +114,5 @@ module.exports = { createWebServer, createServeFile, createServeStaticFile, - createCurrentFiles + createFilesPromise } diff --git a/test/unit/middleware/karma.spec.js b/test/unit/middleware/karma.spec.js index f4a628854..ba2dcb6fe 100644 --- a/test/unit/middleware/karma.spec.js +++ b/test/unit/middleware/karma.spec.js @@ -11,7 +11,7 @@ const HttpRequestMock = mocks.http.ServerRequest describe('middleware.karma', () => { let serveFile - let currentFiles + let currentWebFiles let nextSpy let response @@ -36,7 +36,7 @@ describe('middleware.karma', () => { const createServeFile = require('../../../lib/middleware/common').createServeFile const createKarmaMiddleware = require('../../../lib/middleware/karma').create - let handler = serveFile = currentFiles = nextSpy = response = null + let handler = serveFile = currentWebFiles = nextSpy = response = null const clientConfig = { foo: 'bar' @@ -57,10 +57,10 @@ describe('middleware.karma', () => { beforeEach(() => { nextSpy = sinon.spy() response = new HttpResponseMock() - currentFiles = { files: { included: [], served: [] } } + currentWebFiles = { included: [], served: [] } serveFile = createServeFile(fsMock, '/karma/static') handler = createKarmaMiddleware( - currentFiles, + currentWebFiles, serveFile, null, injector, @@ -70,15 +70,6 @@ describe('middleware.karma', () => { ) }) - // helpers - const includedFiles = (files) => { - currentFiles.files = { included: files, served: [] } - } - - const servedFiles = (files) => { - currentFiles.files = { included: [], served: files } - } - const normalizedHttpRequest = (urlPath) => { const req = new HttpRequestMock(urlPath) req.normalizedUrl = req.url @@ -120,7 +111,7 @@ describe('middleware.karma', () => { it('should serve client.html', (done) => { handler = createKarmaMiddleware( - currentFiles, + currentWebFiles, serveFile, null, injector, @@ -139,7 +130,7 @@ describe('middleware.karma', () => { it('should serve /?id=xxx', (done) => { handler = createKarmaMiddleware( - currentFiles, + currentWebFiles, serveFile, null, injector, @@ -158,7 +149,7 @@ describe('middleware.karma', () => { it('should serve /?x-ua-compatible with replaced values', (done) => { handler = createKarmaMiddleware( - currentFiles, + currentWebFiles, serveFile, null, injector, @@ -176,7 +167,7 @@ describe('middleware.karma', () => { }) it('should serve debug.html/?x-ua-compatible with replaced values', (done) => { - includedFiles([]) + currentWebFiles.included = [] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -199,10 +190,10 @@ describe('middleware.karma', () => { }) it('should serve context.html with replaced script tags', (done) => { - includedFiles([ + currentWebFiles.included = [ new MockFile('/first.js', 'sha123'), new MockFile('/second.js', 'sha456') - ]) + ] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -214,14 +205,14 @@ describe('middleware.karma', () => { }) it('should serve context.html with replaced link tags', (done) => { - includedFiles([ + currentWebFiles.included = [ new MockFile('/first.css', 'sha007'), new MockFile('/second.html', 'sha678'), new MockFile('/third', 'sha111', 'css'), new MockFile('/fourth', 'sha222', 'html'), new Url('http://some.url.com/fifth', 'css'), new Url('http://some.url.com/sixth', 'html') - ]) + ] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -233,10 +224,10 @@ describe('middleware.karma', () => { }) it('should serve context.html with the correct path for the script tags', (done) => { - includedFiles([ + currentWebFiles.included = [ new MockFile('/some/abc/a.js', 'sha'), new MockFile('/base/path/b.js', 'shaaa') - ]) + ] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -248,7 +239,7 @@ describe('middleware.karma', () => { }) it('should serve context.html with the correct path for link tags', (done) => { - includedFiles([ + currentWebFiles.included = [ new MockFile('/some/abc/a.css', 'sha1'), new MockFile('/base/path/b.css', 'sha2'), new MockFile('/some/abc/c.html', 'sha3'), @@ -257,7 +248,7 @@ describe('middleware.karma', () => { new MockFile('/base/path/f', 'sha6', 'css'), new MockFile('/some/abc/g', 'sha7', 'html'), new MockFile('/base/path/h', 'sha8', 'html') - ]) + ] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -269,9 +260,9 @@ describe('middleware.karma', () => { }) it('should serve context.html with included DOM content', (done) => { - currentFiles = { files: { included: [], served: [] } } + currentWebFiles = { included: [], served: [] } handler = createKarmaMiddleware( - currentFiles, + currentWebFiles, serveFile, null, injector, @@ -279,11 +270,11 @@ describe('middleware.karma', () => { '/' ) - includedFiles([ + currentWebFiles.included = [ new MockFile('/some/abc/a.dom', 'sha1', undefined, 'a'), new MockFile('/some/abc/b_test_dom.html', 'sha2', 'dom', 'b'), new MockFile('/some/abc/c', 'sha3', 'dom', 'c') - ]) + ] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -295,7 +286,7 @@ describe('middleware.karma', () => { }) it('should serve context.json with the correct paths for all files', (done) => { - includedFiles([ + currentWebFiles.included = [ new MockFile('/some/abc/a.css', 'sha1'), new MockFile('/base/path/b.css', 'sha2'), new MockFile('/some/abc/c.html', 'sha3'), @@ -304,7 +295,7 @@ describe('middleware.karma', () => { new MockFile('/base/path/f', 'sha6', 'css'), new MockFile('/some/abc/g', 'sha7', 'html'), new MockFile('/base/path/h', 'sha8', 'html') - ]) + ] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -327,9 +318,9 @@ describe('middleware.karma', () => { }) it('should not change urls', (done) => { - includedFiles([ + currentWebFiles.included = [ new Url('http://some.url.com/whatever') - ]) + ] response.once('end', () => { expect(response).to.beServedAs(200, 'CONTEXT\n') @@ -342,7 +333,7 @@ describe('middleware.karma', () => { it('should send non-caching headers for context.html', (done) => { const ZERO_DATE = (new Date(0)).toUTCString() - includedFiles([]) + currentWebFiles.included = [] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -358,11 +349,11 @@ describe('middleware.karma', () => { it('should inline mappings with all served files', (done) => { fsMock._touchFile('/karma/static/context.html', 0, '%MAPPINGS%') - servedFiles([ + currentWebFiles.served = [ new MockFile('/some/abc/a.js', 'sha_a'), new MockFile('/base/path/b.js', 'sha_b'), new MockFile('\\windows\\path\\uuu\\c.js', 'sha_c') - ]) + ] response.once('end', () => { expect(response).to.beServedAs(200, "window.__karma__.files = {\n '/__proxy__/__karma__/absolute/some/abc/a.js': 'sha_a',\n '/__proxy__/__karma__/base/b.js': 'sha_b',\n '/__proxy__/__karma__/absolute\\\\windows\\\\path\\\\uuu\\\\c.js': 'sha_c'\n};\n") @@ -374,10 +365,10 @@ describe('middleware.karma', () => { it('should escape quotes in mappings with all served files', (done) => { fsMock._touchFile('/karma/static/context.html', 0, '%MAPPINGS%') - servedFiles([ + currentWebFiles.served = [ new MockFile("/some/abc/a'b.js", 'sha_a'), new MockFile('/base/path/ba.js', 'sha_b') - ]) + ] response.once('end', () => { expect(response).to.beServedAs(200, 'window.__karma__.files = {\n \'/__proxy__/__karma__/absolute/some/abc/a\\\'b.js\': \'sha_a\',\n \'/__proxy__/__karma__/base/ba.js\': \'sha_b\'\n};\n') @@ -388,10 +379,10 @@ describe('middleware.karma', () => { }) it('should serve debug.html with replaced script tags without timestamps', (done) => { - includedFiles([ + currentWebFiles.included = [ new MockFile('/first.js'), new MockFile('/base/path/b.js') - ]) + ] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -403,7 +394,7 @@ describe('middleware.karma', () => { }) it('should serve debug.html with replaced link tags without timestamps', (done) => { - includedFiles([ + currentWebFiles.included = [ new MockFile('/first.css'), new MockFile('/base/path/b.css'), new MockFile('/second.html'), @@ -412,7 +403,7 @@ describe('middleware.karma', () => { new MockFile('/base/path/f', null, 'css'), new MockFile('/fourth', null, 'html'), new MockFile('/base/path/g', null, 'html') - ]) + ] response.once('end', () => { expect(nextSpy).not.to.have.been.called @@ -424,9 +415,9 @@ describe('middleware.karma', () => { }) it('should inline client config to debug.html', (done) => { - includedFiles([ + currentWebFiles.included = [ new MockFile('/first.js') - ]) + ] fsMock._touchFile('/karma/static/debug.html', 1, '%CLIENT_CONFIG%') response.once('end', () => { @@ -438,7 +429,7 @@ describe('middleware.karma', () => { }) it('should not serve other files even if they are in urlRoot', (done) => { - includedFiles([]) + currentWebFiles.included = [] callHandlerWith('/__karma__/something/else.js', () => { expect(response).to.beNotServed() @@ -449,7 +440,7 @@ describe('middleware.karma', () => { it('should update handle updated configs', (done) => { let i = 0 handler = createKarmaMiddleware( - currentFiles, + currentWebFiles, serveFile, null, { @@ -471,9 +462,9 @@ describe('middleware.karma', () => { { path: '/__proxy__/' } ) - includedFiles([ + currentWebFiles.included = [ new MockFile('/first.js') - ]) + ] fsMock._touchFile('/karma/static/debug.html', 1, '%CLIENT_CONFIG%') response.once('end', () => { diff --git a/test/unit/middleware/source_files.spec.js b/test/unit/middleware/source_files.spec.js index c877cfd11..f272fc678 100644 --- a/test/unit/middleware/source_files.spec.js +++ b/test/unit/middleware/source_files.spec.js @@ -9,8 +9,8 @@ const createSourceFilesMiddleware = require('../../../lib/middleware/source_file describe('middleware.source_files', function () { let next - let currentFiles - let server = next = currentFiles = null + let currentWebFiles + let server = next = currentWebFiles = null const fsMock = mocks.fs.create({ base: { @@ -50,8 +50,8 @@ describe('middleware.source_files', function () { } beforeEach(function () { - currentFiles = { files: { included: [], served: [] } } - server = createServer(currentFiles, serveFile, '/base/path') + currentWebFiles = { included: [], served: [] } + server = createServer(currentWebFiles, serveFile, '/base/path') return server }) @@ -59,15 +59,11 @@ describe('middleware.source_files', function () { return next.resetHistory() }) - function servedFiles (list) { - currentFiles.files = { included: [], served: list } - } - describe('Range headers', function () { beforeEach(function () { - servedFiles([ + currentWebFiles.served = [ new File('/src/some.js') - ]) + ] }) it('allows single explicit ranges', function () { @@ -113,9 +109,9 @@ describe('middleware.source_files', function () { let file beforeEach(function () { file = new File('/src/some.js') - servedFiles([ + currentWebFiles.served = [ file - ]) + ] }) it('serves encoded files', function () { @@ -145,9 +141,9 @@ describe('middleware.source_files', function () { }) it('should serve absolute js source files ignoring timestamp', function () { - servedFiles([ + currentWebFiles.served = [ new File('/src/some.js') - ]) + ] return request(server) .get('/absolute/src/some.js?123345') @@ -155,9 +151,9 @@ describe('middleware.source_files', function () { }) it('should serve js source files from base folder ignoring timestamp', function () { - servedFiles([ + currentWebFiles.served = [ new File('/base/path/a.js') - ]) + ] return request(server) .get('/base/a.js?123345') @@ -168,9 +164,9 @@ describe('middleware.source_files', function () { }) it('should send strict caching headers for js source files with sha', function () { - servedFiles([ + currentWebFiles.served = [ new File('/src/some.js') - ]) + ] return request(server) .get('/absolute/src/some.js?df43b8acf136389a8dd989bda397d1c9b4e048be') @@ -182,9 +178,9 @@ describe('middleware.source_files', function () { }) it('should send strict caching headers for js source files with sha (in basePath)', function () { - servedFiles([ + currentWebFiles.served = [ new File('/base/path/a.js') - ]) + ] return request(server) .get('/base/a.js?df43b8acf136389a8dd989bda397d1c9b4e048be') @@ -195,9 +191,9 @@ describe('middleware.source_files', function () { it('should send no-caching headers for js source files without timestamps', function () { const ZERO_DATE = new RegExp(new Date(0).toUTCString()) - servedFiles([ + currentWebFiles.served = [ new File('/src/some.js') - ]) + ] return request(server) .get('/absolute/src/some.js') @@ -212,7 +208,7 @@ describe('middleware.source_files', function () { }) it('should not serve files that are not in served', function () { - servedFiles([]) + currentWebFiles.served = [] return request(server) .get('/absolute/non-existing.html') @@ -220,9 +216,9 @@ describe('middleware.source_files', function () { }) it('should serve 404 if file is served but does not exist', function () { - servedFiles([ + currentWebFiles.served = [ new File('/non-existing.js') - ]) + ] return request(server) .get('/absolute/non-existing.js') @@ -230,11 +226,11 @@ describe('middleware.source_files', function () { }) it('should serve js source file from base path containing utf8 chars', function () { - servedFiles([ + currentWebFiles.served = [ new File('/utf8ášč/some.js') - ]) + ] - server = createServer(currentFiles, serveFile, '/utf8ášč') + server = createServer(currentWebFiles, serveFile, '/utf8ášč') return request(server) .get('/base/some.js') @@ -245,11 +241,11 @@ describe('middleware.source_files', function () { }) it('should serve js source file from paths containing HTML URL encoded chars', function () { - servedFiles([ + currentWebFiles.served = [ new File('/jenkins%2Fbranch/some.js') - ]) + ] - server = createServer(currentFiles, serveFile, '') + server = createServer(currentWebFiles, serveFile, '') return request(server) .get('/base/jenkins%2Fbranch/some.js') @@ -260,9 +256,9 @@ describe('middleware.source_files', function () { }) it('should set content-type headers', function () { - servedFiles([ + currentWebFiles.served = [ new File('/base/path/index.html') - ]) + ] return request(server) .get('/base/index.html') @@ -274,9 +270,9 @@ describe('middleware.source_files', function () { const cachedFile = new File('/some/file.js') cachedFile.content = 'cached-content' - servedFiles([ + currentWebFiles.served = [ cachedFile - ]) + ] return request(server) .get('/absolute/some/file.js') @@ -291,9 +287,9 @@ describe('middleware.source_files', function () { cachedFile.content = 'cached-content' cachedFile.doNotCache = true - servedFiles([ + currentWebFiles.served = [ cachedFile - ]) + ] return request(server) .get('/absolute/src/some.js') diff --git a/test/unit/web-server.spec.js b/test/unit/web-server.spec.js index 8fcf29d1a..31d851f53 100644 --- a/test/unit/web-server.spec.js +++ b/test/unit/web-server.spec.js @@ -34,8 +34,9 @@ describe('web-server', () => { let customFileHandlers = server = emitter = null let beforeMiddlewareActive = false let middlewareActive = false + const mockCurrentWebFiles = { served: [], included: [] } const servedFiles = (files) => { - emitter.emit('file_list_modified', { included: [], served: files }) + mockCurrentWebFiles.served = files } describe('request', () => { @@ -60,7 +61,7 @@ describe('web-server', () => { customFileHandlers: ['value', customFileHandlers], emitter: ['value', emitter], fileList: ['value', { files: { served: [], included: [] } }], - currentFiles: ['factory', m.createCurrentFiles], + currentWebFiles: ['value', mockCurrentWebFiles], serveStaticFile: ['factory', m.createServeStaticFile], serveFile: ['factory', m.createServeFile], capturedBrowsers: ['value', null], @@ -228,7 +229,7 @@ describe('web-server', () => { customFileHandlers: ['value', customFileHandlers], emitter: ['value', emitter], fileList: ['value', { files: { served: [], included: [] } }], - currentFiles: ['factory', m.createCurrentFiles], + currentWebFiles: ['value', mockCurrentWebFiles], serveStaticFile: ['factory', m.createServeStaticFile], serveFile: ['factory', m.createServeFile], capturedBrowsers: ['value', null], @@ -272,7 +273,7 @@ describe('web-server', () => { customFileHandlers: ['value', customFileHandlers], emitter: ['value', emitter], fileList: ['value', { files: { served: [], included: [] } }], - currentFiles: ['factory', m.createCurrentFiles], + currentWebFiles: ['value', mockCurrentWebFiles], serveStaticFile: ['factory', m.createServeStaticFile], serveFile: ['factory', m.createServeFile], capturedBrowsers: ['value', null], diff --git a/test/unit/web_files.spec.js b/test/unit/web_files.spec.js new file mode 100644 index 000000000..5b7004f5a --- /dev/null +++ b/test/unit/web_files.spec.js @@ -0,0 +1,69 @@ +const createWebFiles = require('../../lib/web-files').createWebFiles + +describe('WebFiles', () => { + it('are created', () => { + const mockConfigBundler = undefined + const mockInstantiatePlugin = sinon.stub() + const mockEmitter = { on: () => {} } + const currentWebFiles = createWebFiles(mockConfigBundler, mockInstantiatePlugin, mockEmitter) + expect(currentWebFiles.included).to.be.deep.equal([]) + expect(currentWebFiles.served).to.be.deep.equal([]) + expect(mockInstantiatePlugin).not.to.be.called + }) + + it('are updated', () => { + const mockConfigBundler = undefined + const mockInstantiatePlugin = sinon.stub() + const mockModifiedWebFiles = { included: ['foo.js'], served: ['bar.js'] } + + let actualCallback + const mockEmitter = { + on: (eventName, callback) => { + expect(eventName).to.be.equal('file_list_modified') + actualCallback = callback + }, + emit: (eventName, wf) => { + expect(eventName).to.be.equal('web_files_modified') + expect(wf).to.be.deep.equal(mockModifiedWebFiles) + } + } + const currentWebFiles = createWebFiles(mockConfigBundler, mockInstantiatePlugin, mockEmitter) + + actualCallback(mockModifiedWebFiles) + + expect(currentWebFiles.included).to.be.deep.equal(['foo.js']) + expect(currentWebFiles.served).to.be.deep.equal(['bar.js']) + expect(mockInstantiatePlugin).not.to.be.called + }) + + it('are bundled', () => { + const mockConfigBundler = 'mockBundler' + const mockModifiedWebFiles = { included: ['foo.js'], served: ['bar.js'] } + const mockBundledWebFiles = { included: ['bundled_foo.js'], served: ['bar.js'] } + const mockInstantiatePlugin = (kind, name) => { + expect(kind).to.be.equal('bundler') + expect(name).to.be.equal('mockBundler') + return (wf) => mockBundledWebFiles + } + + let actualCallback + const mockEmitter = { + on: (eventName, callback) => { + expect(eventName).to.be.equal('file_list_modified') + actualCallback = callback + }, + emit: (eventName, wf) => { + expect(eventName).to.be.equal('web_files_modified') + expect(wf).to.be.deep.equal(mockBundledWebFiles) + } + } + // Model server startup + const currentWebFiles = createWebFiles(mockConfigBundler, mockInstantiatePlugin, mockEmitter) + + // Model preprocessing complete + actualCallback(mockModifiedWebFiles) + + expect(currentWebFiles.included).to.be.deep.equal(['bundled_foo.js']) + expect(currentWebFiles.served).to.be.deep.equal(['bar.js']) + }) +})