/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ import { PassThrough } from 'stream'; import * as Rx from 'rxjs'; import { toArray } from 'rxjs/operators'; import { OptimizerUpdate } from '@kbn/optimizer'; import { observeLines, createReplaceSerializer } from '@kbn/dev-utils'; import { firstValueFrom } from '@kbn/std'; import { Optimizer, Options } from './optimizer'; jest.mock('@kbn/optimizer'); const realOptimizer = jest.requireActual('@kbn/optimizer'); const { runOptimizer, OptimizerConfig, logOptimizerState } = jest.requireMock('@kbn/optimizer'); logOptimizerState.mockImplementation(realOptimizer.logOptimizerState); class MockOptimizerConfig {} const mockOptimizerUpdate = (phase: OptimizerUpdate['state']['phase']) => { return { state: { compilerStates: [], durSec: 0, offlineBundles: [], onlineBundles: [], phase, startTime: 100, }, }; }; const defaultOptions: Options = { enabled: true, cache: true, dist: true, oss: true, pluginPaths: ['/some/dir'], pluginScanDirs: ['/some-scan-path'], quiet: true, silent: true, repoRoot: '/app', runExamples: true, watch: true, }; function setup(options: Options = defaultOptions) { const update$ = new Rx.Subject(); OptimizerConfig.create.mockImplementation(() => new MockOptimizerConfig()); runOptimizer.mockImplementation(() => update$); const optimizer = new Optimizer(options); return { optimizer, update$ }; } const subscriptions: Rx.Subscription[] = []; expect.addSnapshotSerializer(createReplaceSerializer(/\[\d\d:\d\d:\d\d\.\d\d\d\]/, '[timestamp]')); afterEach(() => { for (const sub of subscriptions) { sub.unsubscribe(); } subscriptions.length = 0; jest.clearAllMocks(); }); it('uses options to create valid OptimizerConfig', () => { setup(); setup({ ...defaultOptions, cache: false, dist: false, runExamples: false, oss: false, pluginPaths: [], pluginScanDirs: [], repoRoot: '/foo/bar', watch: false, }); expect(OptimizerConfig.create.mock.calls).toMatchInlineSnapshot(` Array [ Array [ Object { "cache": true, "dist": true, "examples": true, "includeCoreBundle": true, "oss": true, "pluginPaths": Array [ "/some/dir", ], "pluginScanDirs": Array [ "/some-scan-path", ], "repoRoot": "/app", "watch": true, }, ], Array [ Object { "cache": false, "dist": false, "examples": false, "includeCoreBundle": true, "oss": false, "pluginPaths": Array [], "pluginScanDirs": Array [], "repoRoot": "/foo/bar", "watch": false, }, ], ] `); }); it('is ready when optimizer phase is success or issue and logs in familiar format', async () => { const writeLogTo = new PassThrough(); const linesPromise = firstValueFrom(observeLines(writeLogTo).pipe(toArray())); const { update$, optimizer } = setup({ ...defaultOptions, quiet: false, silent: false, writeLogTo, }); const history: any[] = ['']; subscriptions.push( optimizer.isReady$().subscribe({ next(ready) { history.push(`ready: ${ready}`); }, error(error) { throw error; }, complete() { history.push(`complete`); }, }) ); subscriptions.push( optimizer.run$.subscribe({ error(error) { throw error; }, }) ); history.push(''); update$.next(mockOptimizerUpdate('success')); history.push(''); update$.next(mockOptimizerUpdate('running')); history.push(''); update$.next(mockOptimizerUpdate('issue')); update$.complete(); expect(history).toMatchInlineSnapshot(` Array [ "", "", "ready: true", "", "ready: false", "", "ready: true", ] `); writeLogTo.end(); const lines = await linesPromise; expect(lines).toMatchInlineSnapshot(` Array [ " np bld log [timestamp] [success][@kbn/optimizer] 0 bundles compiled successfully after 0 sec", " np bld log [timestamp] [error][@kbn/optimizer] webpack compile errors", ] `); }); it('completes immedately and is immediately ready when disabled', () => { const ready$ = new Rx.BehaviorSubject(undefined); const { optimizer, update$ } = setup({ ...defaultOptions, enabled: false, }); subscriptions.push(optimizer.isReady$().subscribe(ready$)); expect(update$.observers).toHaveLength(0); expect(runOptimizer).not.toHaveBeenCalled(); expect(ready$).toHaveProperty('isStopped', true); expect(ready$.getValue()).toBe(true); });