Migrate all Node.js unit tests to Karma

Fix: 290183109
Test: npm run test:presubmit
Change-Id: I646fdfa60fe770c0b959ed01b77e41c2ebdf7bbf
diff --git a/tools/winscope/karma.config.common.js b/tools/winscope/karma.config.common.js
index 3e98f69..b288ab2 100644
--- a/tools/winscope/karma.config.common.js
+++ b/tools/winscope/karma.config.common.js
@@ -22,7 +22,7 @@
     frameworks: ['jasmine', 'webpack'],
     plugins: ['karma-webpack', 'karma-chrome-launcher', 'karma-jasmine', 'karma-sourcemap-loader'],
     files: [
-      {pattern: 'src/main_component_test.ts', watched: false},
+      {pattern: 'src/main_unit_test.ts', watched: false},
       {pattern: 'src/test/fixtures/**/*', included: false, served: true},
       {
         pattern: 'deps_build/trace_processor/to_be_served/engine_bundle.js',
@@ -36,7 +36,7 @@
       },
     ],
     preprocessors: {
-      'src/main_component_test.ts': ['webpack', 'sourcemap'],
+      'src/main_unit_test.ts': ['webpack', 'sourcemap'],
     },
     webpack: webpackConfig,
   });
diff --git a/tools/winscope/package.json b/tools/winscope/package.json
index fd28173..70d8432 100644
--- a/tools/winscope/package.json
+++ b/tools/winscope/package.json
@@ -16,13 +16,12 @@
     "build:trace_processor": "PERFETTO_TOP=../../../external/perfetto; (cd $PERFETTO_TOP && tools/install-build-deps --ui && ui/node ui/build.js --out trace_processor_build) && rm -rf deps_build/trace_processor && mkdir -p deps_build/trace_processor && rsync -ar $PERFETTO_TOP/trace_processor_build/ deps_build/trace_processor && mkdir deps_build/trace_processor/to_be_served && cp deps_build/trace_processor/ui/dist_version/engine_bundle.js deps_build/trace_processor/to_be_served/ && cp deps_build/trace_processor/wasm/trace_processor.wasm deps_build/trace_processor/to_be_served/",
     "build:prod": "npm run build:trace_processor && npm run build:kotlin && webpack --config webpack.config.prod.js --progress && cp deps_build/trace_processor/to_be_served/* dist/prod/",
     "install:chromedriver": "chrome_version=$(google-chrome --version |cut -d' ' -f3); rm -rf deps_build/chromedriver-linux64 && mkdir -p deps_build/chromedriver-linux64 && (cd deps_build/chromedriver-linux64 && wget https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/${chrome_version}/linux64/chromedriver-linux64.zip && cd .. && unzip chromedriver-linux64/chromedriver-linux64.zip)",
-    "test:unit": "webpack --config webpack.config.unit_test.js && jasmine dist/unit_test/bundle.js",
-    "test:component:ci": "npx karma start karma.config.ci.js",
-    "test:component:dev": "npx karma start karma.config.dev.js",
+    "test:unit:ci": "npx karma start karma.config.ci.js",
+    "test:unit:dev": "npx karma start karma.config.dev.js",
     "test:e2e": "npm run install:chromedriver && rm -rf dist/e2e_test && npx tsc -p ./src/test/e2e && npx protractor protractor.config.js",
-    "test:presubmit:quiet": "npm run test:unit && npm run test:component:ci && npm run format:check && npm run tslint:check && npm run eslint:check && npm run deps_graph:check_cycles",
+    "test:presubmit:quiet": "npm run test:unit:ci && npm run format:check && npm run tslint:check && npm run eslint:check && npm run deps_graph:check_cycles",
     "test:presubmit": "(npm run test:presubmit:quiet && printf '\\033[1m\\033[32mALL GREEN! \\U1F49A (Kean loves you)\\n') || (printf '\\033[1m\\033[31mFAILING! \\U1F92F (Kean is upset)\\n' && false)",
-    "test:all": "npm run test:unit && npm run test:component:ci && npm run test:e2e && npm run format:check && npm run tslint:check && npm run eslint:check && npm run deps_graph:check_cycles"
+    "test:all": "npm run test:unit:ci && npm run test:e2e && npm run format:check && npm run tslint:check && npm run eslint:check && npm run deps_graph:check_cycles"
   },
   "private": true,
   "dependencies": {
diff --git a/tools/winscope/protractor.config.js b/tools/winscope/protractor.config.js
index 00e8eeb..708f555 100644
--- a/tools/winscope/protractor.config.js
+++ b/tools/winscope/protractor.config.js
@@ -21,7 +21,7 @@
 // and change the hardcoded version here
 
 exports.config = {
-  specs: ['dist/e2e_test/e2e/*_test.js'],
+  specs: ['dist/e2e_test/*_test.js'],
 
   directConnect: true,
   capabilities: {
diff --git a/tools/winscope/src/app/components/adb_proxy_component_test.ts b/tools/winscope/src/app/components/adb_proxy_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/adb_proxy_component_test.ts
rename to tools/winscope/src/app/components/adb_proxy_test.ts
diff --git a/tools/winscope/src/app/components/app_component_test.ts b/tools/winscope/src/app/components/app_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/app_component_test.ts
rename to tools/winscope/src/app/components/app_test.ts
diff --git a/tools/winscope/src/app/components/collect_traces_component_test.ts b/tools/winscope/src/app/components/collect_traces_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/collect_traces_component_test.ts
rename to tools/winscope/src/app/components/collect_traces_test.ts
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/canvas_drawer_component_test.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/canvas_drawer_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/timeline/expanded-timeline/canvas_drawer_component_test.ts
rename to tools/winscope/src/app/components/timeline/expanded-timeline/canvas_drawer_test.ts
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component_test.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component_test.ts
rename to tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_test.ts
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts
rename to tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_test.ts
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts
rename to tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_test.ts
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component_test.ts b/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component_test.ts
rename to tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_test.ts
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/slider_component_test.ts b/tools/winscope/src/app/components/timeline/mini-timeline/slider_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/timeline/mini-timeline/slider_component_test.ts
rename to tools/winscope/src/app/components/timeline/mini-timeline/slider_test.ts
diff --git a/tools/winscope/src/app/components/timeline/timeline_component_test.ts b/tools/winscope/src/app/components/timeline/timeline_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/timeline/timeline_component_test.ts
rename to tools/winscope/src/app/components/timeline/timeline_test.ts
diff --git a/tools/winscope/src/app/components/trace_config_component_test.ts b/tools/winscope/src/app/components/trace_config_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/trace_config_component_test.ts
rename to tools/winscope/src/app/components/trace_config_test.ts
diff --git a/tools/winscope/src/app/components/trace_view_component_test.ts b/tools/winscope/src/app/components/trace_view_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/trace_view_component_test.ts
rename to tools/winscope/src/app/components/trace_view_test.ts
diff --git a/tools/winscope/src/app/components/upload_traces_component_test.ts b/tools/winscope/src/app/components/upload_traces_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/upload_traces_component_test.ts
rename to tools/winscope/src/app/components/upload_traces_test.ts
diff --git a/tools/winscope/src/app/components/web_adb_component_test.ts b/tools/winscope/src/app/components/web_adb_test.ts
similarity index 100%
rename from tools/winscope/src/app/components/web_adb_component_test.ts
rename to tools/winscope/src/app/components/web_adb_test.ts
diff --git a/tools/winscope/src/app/trace_file_filter_test.ts b/tools/winscope/src/app/trace_file_filter_test.ts
index 5db1a4b..c62f5c0 100644
--- a/tools/winscope/src/app/trace_file_filter_test.ts
+++ b/tools/winscope/src/app/trace_file_filter_test.ts
@@ -15,7 +15,6 @@
  */
 
 import {ParserError, ParserErrorType} from 'parsers/parser_factory';
-import {FileImpl} from 'test/common/file_impl';
 import {UnitTestUtils} from 'test/unit/utils';
 import {TraceFile} from 'trace/trace_file';
 import {TraceFileFilter} from './trace_file_filter';
@@ -24,11 +23,7 @@
   const filter = new TraceFileFilter();
 
   // Could be any file, we just need an instance of File to be used as a fake bugreport archive
-  // TODO(b/290183109): use the Web API's File type instead, once this Node.js test is ported to Karma.
-  const bugreportArchive = new FileImpl(
-    new ArrayBuffer(0),
-    'test_bugreport.zip'
-  ) as unknown as File;
+  const bugreportArchive = new File([new ArrayBuffer(0)], 'test_bugreport.zip') as unknown as File;
 
   describe('bugreport (detects it is a bugreport)', () => {
     it('ignores non-trace dirs', async () => {
@@ -134,7 +129,7 @@
 
   const makeTraceFile = (filename: string, parentArchive?: File, size?: number) => {
     size = size ?? 0;
-    const file = new FileImpl(new ArrayBuffer(size), filename);
+    const file = new File([new ArrayBuffer(size)], filename);
     return new TraceFile(file as unknown as File, parentArchive);
   };
 
diff --git a/tools/winscope/src/app/trace_pipeline_component_test.ts b/tools/winscope/src/app/trace_pipeline_test.ts
similarity index 87%
rename from tools/winscope/src/app/trace_pipeline_component_test.ts
rename to tools/winscope/src/app/trace_pipeline_test.ts
index c02e8f8..7607b3e 100644
--- a/tools/winscope/src/app/trace_pipeline_component_test.ts
+++ b/tools/winscope/src/app/trace_pipeline_test.ts
@@ -17,8 +17,8 @@
 import {assertDefined} from 'common/assert_utils';
 import {FileUtils} from 'common/file_utils';
 import {ParserError, ParserErrorType} from 'parsers/parser_factory';
-import {KarmaTestUtils} from 'test/unit/karma_utils';
 import {TracesUtils} from 'test/unit/traces_utils';
+import {UnitTestUtils} from 'test/unit/utils';
 import {TraceFile} from 'trace/trace_file';
 import {TraceType} from 'trace/trace_type';
 import {TracePipeline} from './trace_pipeline';
@@ -30,10 +30,10 @@
 
   beforeEach(async () => {
     validSfTraceFile = new TraceFile(
-      await KarmaTestUtils.getFixtureFile('traces/elapsed_and_real_timestamp/SurfaceFlinger.pb')
+      await UnitTestUtils.getFixtureFile('traces/elapsed_and_real_timestamp/SurfaceFlinger.pb')
     );
     validWmTraceFile = new TraceFile(
-      await KarmaTestUtils.getFixtureFile('traces/elapsed_and_real_timestamp/WindowManager.pb')
+      await UnitTestUtils.getFixtureFile('traces/elapsed_and_real_timestamp/WindowManager.pb')
     );
     tracePipeline = new TracePipeline();
   });
@@ -67,45 +67,45 @@
     expect(tracePipeline.getTraces().getSize()).toEqual(0);
 
     // Could be any file, we just need an instance of File to be used as a fake bugreport archive
-    const bugreportArchive = await KarmaTestUtils.getFixtureFile(
+    const bugreportArchive = await UnitTestUtils.getFixtureFile(
       'bugreports/bugreport_stripped.zip'
     );
 
     const bugreportFiles = [
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile('bugreports/main_entry.txt', 'main_entry.txt'),
+        await UnitTestUtils.getFixtureFile('bugreports/main_entry.txt', 'main_entry.txt'),
         bugreportArchive
       ),
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile(
+        await UnitTestUtils.getFixtureFile(
           'bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
           'bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt'
         ),
         bugreportArchive
       ),
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile(
+        await UnitTestUtils.getFixtureFile(
           'traces/elapsed_and_real_timestamp/SurfaceFlinger.pb',
           'FS/data/misc/wmtrace/surface_flinger.bp'
         ),
         bugreportArchive
       ),
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile(
+        await UnitTestUtils.getFixtureFile(
           'traces/elapsed_and_real_timestamp/Transactions.pb',
           'FS/data/misc/wmtrace/transactions.bp'
         ),
         bugreportArchive
       ),
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile(
+        await UnitTestUtils.getFixtureFile(
           'traces/elapsed_and_real_timestamp/WindowManager.pb',
           'proto/window_CRITICAL.proto'
         ),
         bugreportArchive
       ),
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile(
+        await UnitTestUtils.getFixtureFile(
           'traces/elapsed_and_real_timestamp/wm_transition_trace.pb',
           'FS/data/misc/ignored-dir/wm_transition_trace.bp'
         ),
@@ -120,7 +120,7 @@
     // The even weirder corner case where two bugreports are loaded at the same time is
     // currently not properly handled.
     const plainTraceFile = new TraceFile(
-      await KarmaTestUtils.getFixtureFile(
+      await UnitTestUtils.getFixtureFile(
         'traces/elapsed_and_real_timestamp/InputMethodClients.pb',
         'would-be-ignored-if-was-part-of-bugreport/input_method_clients.pb'
       )
@@ -141,7 +141,7 @@
 
   it('is robust to invalid trace files', async () => {
     const invalidTraceFiles = [
-      new TraceFile(await KarmaTestUtils.getFixtureFile('winscope_homepage.png')),
+      new TraceFile(await UnitTestUtils.getFixtureFile('winscope_homepage.png')),
     ];
 
     const errors = await tracePipeline.loadTraceFiles(invalidTraceFiles);
@@ -153,8 +153,8 @@
   it('is robust to mixed valid and invalid trace files', async () => {
     expect(tracePipeline.getTraces().getSize()).toEqual(0);
     const files = [
-      new TraceFile(await KarmaTestUtils.getFixtureFile('winscope_homepage.png')),
-      new TraceFile(await KarmaTestUtils.getFixtureFile('traces/dump_WindowManager.pb')),
+      new TraceFile(await UnitTestUtils.getFixtureFile('winscope_homepage.png')),
+      new TraceFile(await UnitTestUtils.getFixtureFile('traces/dump_WindowManager.pb')),
     ];
     const errors = await tracePipeline.loadTraceFiles(files);
     await tracePipeline.buildTraces();
@@ -164,7 +164,7 @@
 
   it('is robust to trace files with no entries', async () => {
     const traceFilesWithNoEntries = [
-      new TraceFile(await KarmaTestUtils.getFixtureFile('traces/no_entries_InputMethodClients.pb')),
+      new TraceFile(await UnitTestUtils.getFixtureFile('traces/no_entries_InputMethodClients.pb')),
     ];
 
     const errors = await tracePipeline.loadTraceFiles(traceFilesWithNoEntries);
@@ -178,13 +178,13 @@
   it('is robust to multiple trace files of same type', async () => {
     const traceFilesOfSameType = [
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile(
+        await UnitTestUtils.getFixtureFile(
           'traces/elapsed_and_real_timestamp/SurfaceFlinger.pb',
           'file0.pb'
         )
       ),
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile(
+        await UnitTestUtils.getFixtureFile(
           'traces/elapsed_and_real_timestamp/SurfaceFlinger.pb',
           'file1.pb'
         )
@@ -242,7 +242,7 @@
   it('gets screenrecording data', async () => {
     const traceFiles = [
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile(
+        await UnitTestUtils.getFixtureFile(
           'traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4'
         )
       ),
@@ -258,12 +258,12 @@
   it('creates zip archive with loaded trace files', async () => {
     const traceFiles = [
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile(
+        await UnitTestUtils.getFixtureFile(
           'traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4'
         )
       ),
       new TraceFile(
-        await KarmaTestUtils.getFixtureFile('traces/perfetto/transactions_trace.perfetto-trace')
+        await UnitTestUtils.getFixtureFile('traces/perfetto/transactions_trace.perfetto-trace')
       ),
     ];
     await tracePipeline.loadTraceFiles(traceFiles);
diff --git a/tools/winscope/src/main_component_test.ts b/tools/winscope/src/main_unit_test.ts
similarity index 87%
rename from tools/winscope/src/main_component_test.ts
rename to tools/winscope/src/main_unit_test.ts
index f6c90fd..3a2b8de 100644
--- a/tools/winscope/src/main_component_test.ts
+++ b/tools/winscope/src/main_unit_test.ts
@@ -36,6 +36,6 @@
 
 TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
 
-// load all tests of Angular components
-const context = require.context('./', true, /_component_test\.ts$/);
+// filter matches all "*_test.ts" files that are not within the /test/e2e/ directory
+const context = require.context('./', true, /(?<!\/test\/e2e\/.*)_test.ts$/);
 context.keys().forEach(context);
diff --git a/tools/winscope/src/parsers/parser_common_test.ts b/tools/winscope/src/parsers/parser_common_test.ts
index 115e8c0..0161bfe 100644
--- a/tools/winscope/src/parsers/parser_common_test.ts
+++ b/tools/winscope/src/parsers/parser_common_test.ts
@@ -15,7 +15,6 @@
  */
 import {Timestamp, TimestampType} from 'common/time';
 import {WindowManagerState} from 'flickerlib/windows/WindowManagerState';
-import {CommonTestUtils} from 'test/common/utils';
 import {UnitTestUtils} from 'test/unit/utils';
 import {Parser} from 'trace/parser';
 import {TraceFile} from 'trace/trace_file';
@@ -24,7 +23,7 @@
 
 describe('Parser', () => {
   it('is robust to empty trace file', async () => {
-    const trace = new TraceFile(await CommonTestUtils.getFixtureFile('traces/empty.pb'), undefined);
+    const trace = new TraceFile(await UnitTestUtils.getFixtureFile('traces/empty.pb'), undefined);
     const [parsers, errors] = await new ParserFactory().createParsers([trace]);
     expect(parsers.length).toEqual(0);
   });
diff --git a/tools/winscope/src/parsers/perfetto/abstract_parser_component_test.ts b/tools/winscope/src/parsers/perfetto/abstract_parser_test.ts
similarity index 86%
rename from tools/winscope/src/parsers/perfetto/abstract_parser_component_test.ts
rename to tools/winscope/src/parsers/perfetto/abstract_parser_test.ts
index bde4294..dba9fed 100644
--- a/tools/winscope/src/parsers/perfetto/abstract_parser_component_test.ts
+++ b/tools/winscope/src/parsers/perfetto/abstract_parser_test.ts
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {KarmaTestUtils} from 'test/unit/karma_utils';
+import {UnitTestUtils} from 'test/unit/utils';
 import {TraceType} from 'trace/trace_type';
 
 describe('Perfetto AbstractParser', () => {
   it('fails parsing if there are no trace entries', async () => {
-    const parsers = await KarmaTestUtils.getPerfettoParsers(
+    const parsers = await UnitTestUtils.getPerfettoParsers(
       'traces/perfetto/no_winscope_traces.perfetto-trace'
     );
     expect(parsers.length).toEqual(0);
@@ -26,7 +26,7 @@
 
   it('retrieves partial trace entries', async () => {
     {
-      const parser = await KarmaTestUtils.getPerfettoParser(
+      const parser = await UnitTestUtils.getPerfettoParser(
         TraceType.SURFACE_FLINGER,
         'traces/perfetto/layers_trace.perfetto-trace'
       );
@@ -34,7 +34,7 @@
       expect(entries).toEqual([{vsyncId: 4891n}, {vsyncId: 5235n}, {vsyncId: 5748n}]);
     }
     {
-      const parser = await KarmaTestUtils.getPerfettoParser(
+      const parser = await UnitTestUtils.getPerfettoParser(
         TraceType.TRANSACTIONS,
         'traces/perfetto/transactions_trace.perfetto-trace'
       );
diff --git a/tools/winscope/src/parsers/perfetto/parser_surface_flinger_component_test.ts b/tools/winscope/src/parsers/perfetto/parser_surface_flinger_test.ts
similarity index 96%
rename from tools/winscope/src/parsers/perfetto/parser_surface_flinger_component_test.ts
rename to tools/winscope/src/parsers/perfetto/parser_surface_flinger_test.ts
index e8b75c3..6568fc8 100644
--- a/tools/winscope/src/parsers/perfetto/parser_surface_flinger_component_test.ts
+++ b/tools/winscope/src/parsers/perfetto/parser_surface_flinger_test.ts
@@ -17,7 +17,7 @@
 import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'common/time';
 import {Layer} from 'flickerlib/common';
 import {LayerTraceEntry} from 'flickerlib/layers/LayerTraceEntry';
-import {KarmaTestUtils} from 'test/unit/karma_utils';
+import {UnitTestUtils} from 'test/unit/utils';
 import {Parser} from 'trace/parser';
 import {TraceType} from 'trace/trace_type';
 
@@ -25,7 +25,7 @@
   let parser: Parser<LayerTraceEntry>;
 
   beforeAll(async () => {
-    parser = await KarmaTestUtils.getPerfettoParser(
+    parser = await UnitTestUtils.getPerfettoParser(
       TraceType.SURFACE_FLINGER,
       'traces/perfetto/layers_trace.perfetto-trace'
     );
diff --git a/tools/winscope/src/parsers/perfetto/parser_transactions_component_test.ts b/tools/winscope/src/parsers/perfetto/parser_transactions_test.ts
similarity index 96%
rename from tools/winscope/src/parsers/perfetto/parser_transactions_component_test.ts
rename to tools/winscope/src/parsers/perfetto/parser_transactions_test.ts
index 62a01dd..11fbe93 100644
--- a/tools/winscope/src/parsers/perfetto/parser_transactions_component_test.ts
+++ b/tools/winscope/src/parsers/perfetto/parser_transactions_test.ts
@@ -15,7 +15,7 @@
  */
 import {assertDefined} from 'common/assert_utils';
 import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'common/time';
-import {KarmaTestUtils} from 'test/unit/karma_utils';
+import {UnitTestUtils} from 'test/unit/utils';
 import {Parser} from 'trace/parser';
 import {TraceType} from 'trace/trace_type';
 
@@ -23,7 +23,7 @@
   let parser: Parser<object>;
 
   beforeAll(async () => {
-    parser = await KarmaTestUtils.getPerfettoParser(
+    parser = await UnitTestUtils.getPerfettoParser(
       TraceType.TRANSACTIONS,
       'traces/perfetto/transactions_trace.perfetto-trace'
     );
diff --git a/tools/winscope/src/test/common/file_impl.ts b/tools/winscope/src/test/common/file_impl.ts
deleted file mode 100644
index d4ce943..0000000
--- a/tools/winscope/src/test/common/file_impl.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// This class is needed for unit tests because Node.js doesn't provide
-// an implementation of the Web API's File type
-
-// TODO(b/290183109): remove this class and use the Web API's File type instead,
-//  once all the Node.js tests have been ported to Karma.
-class FileImpl {
-  readonly size: number;
-  readonly type: string;
-  readonly name: string;
-  readonly lastModified: number = 0;
-  readonly webkitRelativePath: string = '';
-  private readonly buffer: ArrayBuffer;
-
-  constructor(buffer: ArrayBuffer, fileName: string) {
-    this.buffer = buffer;
-    this.size = this.buffer.byteLength;
-    this.type = 'application/octet-stream';
-    this.name = fileName;
-  }
-
-  arrayBuffer(): Promise<ArrayBuffer> {
-    return new Promise<ArrayBuffer>((resolve) => {
-      resolve(this.buffer);
-    });
-  }
-
-  slice(start?: number, end?: number, contentType?: string): Blob {
-    throw new Error('Not implemented!');
-  }
-
-  stream(): any {
-    throw new Error('Not implemented!');
-  }
-
-  text(): Promise<string> {
-    const utf8Decoder = new TextDecoder();
-    const text = utf8Decoder.decode(this.buffer);
-    return new Promise<string>((resolve) => {
-      resolve(text);
-    });
-  }
-}
-
-export {FileImpl};
diff --git a/tools/winscope/src/test/common/utils.ts b/tools/winscope/src/test/common/utils.ts
deleted file mode 100644
index 8c81a63..0000000
--- a/tools/winscope/src/test/common/utils.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import * as fs from 'fs';
-import * as path from 'path';
-import {FileImpl} from './file_impl';
-
-class CommonTestUtils {
-  static async getFixtureFile(
-    srcFilename: string,
-    dstFilename: string = srcFilename
-  ): Promise<File> {
-    const buffer = CommonTestUtils.loadFixture(srcFilename);
-    return new FileImpl(buffer, dstFilename) as unknown as File;
-  }
-
-  static loadFixture(filename: string): ArrayBuffer {
-    return fs.readFileSync(CommonTestUtils.getFixturePath(filename));
-  }
-
-  static getFixturePath(filename: string): string {
-    if (path.isAbsolute(filename)) {
-      return filename;
-    }
-    return path.join(CommonTestUtils.getProjectRootPath(), 'src/test/fixtures', filename);
-  }
-
-  static getProjectRootPath(): string {
-    let root = __dirname;
-    while (path.basename(root) !== 'winscope') {
-      root = path.dirname(root);
-    }
-    return root;
-  }
-}
-
-export {CommonTestUtils};
diff --git a/tools/winscope/src/test/e2e/utils.ts b/tools/winscope/src/test/e2e/utils.ts
index 685d94b..2276e26 100644
--- a/tools/winscope/src/test/e2e/utils.ts
+++ b/tools/winscope/src/test/e2e/utils.ts
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import * as path from 'path';
 import {browser, by, element} from 'protractor';
-import {CommonTestUtils} from '../common/utils';
 
-class E2eTestUtils extends CommonTestUtils {
+class E2eTestUtils {
   static readonly WINSCOPE_URL = 'http://localhost:8080';
   static readonly REMOTE_TOOL_MOCK_URL = 'http://localhost:8081';
 
@@ -28,14 +28,6 @@
     }
   }
 
-  static async uploadFixture(...paths: string[]) {
-    const inputFile = element(by.css('input[type="file"]'));
-
-    // Uploading multiple files is not properly supported but
-    // chrome handles file paths joined with new lines
-    await inputFile.sendKeys(paths.map((it) => E2eTestUtils.getFixturePath(it)).join('\n'));
-  }
-
   static async clickViewTracesButton() {
     const button = element(by.css('.load-btn'));
     await button.click();
@@ -48,6 +40,29 @@
       await closeButton.click();
     }
   }
+
+  static async uploadFixture(...paths: string[]) {
+    const inputFile = element(by.css('input[type="file"]'));
+
+    // Uploading multiple files is not properly supported but
+    // chrome handles file paths joined with new lines
+    await inputFile.sendKeys(paths.map((it) => E2eTestUtils.getFixturePath(it)).join('\n'));
+  }
+
+  static getFixturePath(filename: string): string {
+    if (path.isAbsolute(filename)) {
+      return filename;
+    }
+    return path.join(E2eTestUtils.getProjectRootPath(), 'src/test/fixtures', filename);
+  }
+
+  private static getProjectRootPath(): string {
+    let root = __dirname;
+    while (path.basename(root) !== 'winscope') {
+      root = path.dirname(root);
+    }
+    return root;
+  }
 }
 
 export {E2eTestUtils};
diff --git a/tools/winscope/src/test/unit/karma_utils.ts b/tools/winscope/src/test/unit/karma_utils.ts
deleted file mode 100644
index 09c02c4..0000000
--- a/tools/winscope/src/test/unit/karma_utils.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {assertDefined} from 'common/assert_utils';
-import {UrlUtils} from 'common/url_utils';
-import {ParserFactory as PerfettoParserFactory} from 'parsers/perfetto/parser_factory';
-import {Parser} from 'trace/parser';
-import {TraceFile} from 'trace/trace_file';
-import {TraceType} from 'trace/trace_type';
-
-//TODO(b/290183109): unify with UnitTestUtils, once all the Node.js tests have been ported to Karma.
-class KarmaTestUtils {
-  static async getPerfettoParser(
-    traceType: TraceType,
-    fixturePath: string
-  ): Promise<Parser<object>> {
-    const parsers = await KarmaTestUtils.getPerfettoParsers(fixturePath);
-    const parser = assertDefined(parsers.find((parser) => parser.getTraceType() === traceType));
-    return parser;
-  }
-
-  static async getPerfettoParsers(fixturePath: string): Promise<Array<Parser<object>>> {
-    const file = await KarmaTestUtils.getFixtureFile(fixturePath);
-    const traceFile = new TraceFile(file);
-    return await new PerfettoParserFactory().createParsers(traceFile);
-  }
-
-  static async getFixtureFile(
-    srcFilename: string,
-    dstFilename: string = srcFilename
-  ): Promise<File> {
-    const url = UrlUtils.getRootUrl() + 'base/src/test/fixtures/' + srcFilename;
-    const response = await fetch(url);
-    expect(response.ok).toBeTrue();
-    const blob = await response.blob();
-    const file = new File([blob], dstFilename);
-    return file;
-  }
-}
-
-export {KarmaTestUtils};
diff --git a/tools/winscope/src/test/unit/utils.ts b/tools/winscope/src/test/unit/utils.ts
index 79d258c..ed3cdf7 100644
--- a/tools/winscope/src/test/unit/utils.ts
+++ b/tools/winscope/src/test/unit/utils.ts
@@ -14,31 +14,32 @@
  * limitations under the License.
  */
 
+import {assertDefined} from 'common/assert_utils';
 import {TimestampType} from 'common/time';
+import {UrlUtils} from 'common/url_utils';
 import {LayerTraceEntry, WindowManagerState} from 'flickerlib/common';
 import {ParserFactory} from 'parsers/parser_factory';
+import {ParserFactory as PerfettoParserFactory} from 'parsers/perfetto/parser_factory';
 import {TracesParserFactory} from 'parsers/traces_parser_factory';
-import {CommonTestUtils} from 'test/common/utils';
 import {Parser} from 'trace/parser';
-import {Trace} from 'trace/trace';
 import {TraceFile} from 'trace/trace_file';
 import {TraceType} from 'trace/trace_type';
 
-class UnitTestUtils extends CommonTestUtils {
-  static async getTraceFromFile(filename: string): Promise<Trace<object>> {
-    const parser = await UnitTestUtils.getParser(filename);
-
-    const trace = Trace.newUninitializedTrace(parser);
-    trace.init(
-      parser.getTimestamps(TimestampType.REAL) !== undefined
-        ? TimestampType.REAL
-        : TimestampType.ELAPSED
-    );
-    return trace;
+class UnitTestUtils {
+  static async getFixtureFile(
+    srcFilename: string,
+    dstFilename: string = srcFilename
+  ): Promise<File> {
+    const url = UrlUtils.getRootUrl() + 'base/src/test/fixtures/' + srcFilename;
+    const response = await fetch(url);
+    expect(response.ok).toBeTrue();
+    const blob = await response.blob();
+    const file = new File([blob], dstFilename);
+    return file;
   }
 
   static async getParser(filename: string): Promise<Parser<object>> {
-    const file = new TraceFile(await CommonTestUtils.getFixtureFile(filename), undefined);
+    const file = new TraceFile(await UnitTestUtils.getFixtureFile(filename), undefined);
     const [parsers, errors] = await new ParserFactory().createParsers([file]);
     expect(parsers.length)
       .withContext(`Should have been able to create a parser for ${filename}`)
@@ -46,6 +47,21 @@
     return parsers[0].parser;
   }
 
+  static async getPerfettoParser(
+    traceType: TraceType,
+    fixturePath: string
+  ): Promise<Parser<object>> {
+    const parsers = await UnitTestUtils.getPerfettoParsers(fixturePath);
+    const parser = assertDefined(parsers.find((parser) => parser.getTraceType() === traceType));
+    return parser;
+  }
+
+  static async getPerfettoParsers(fixturePath: string): Promise<Array<Parser<object>>> {
+    const file = await UnitTestUtils.getFixtureFile(fixturePath);
+    const traceFile = new TraceFile(file);
+    return await new PerfettoParserFactory().createParsers(traceFile);
+  }
+
   static async getTracesParser(filenames: string[]): Promise<Parser<object>> {
     const parsers = await Promise.all(
       filenames.map((filename) => UnitTestUtils.getParser(filename))
diff --git a/tools/winscope/src/test/utils.ts b/tools/winscope/src/test/utils.ts
index c449a8c..5632e50 100644
--- a/tools/winscope/src/test/utils.ts
+++ b/tools/winscope/src/test/utils.ts
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/290183109): merge with the other unit test utilities, once NodeJs tests are migrated to Karma.
-
 import {ComponentFixture, flush} from '@angular/core/testing';
 
 export function dispatchMouseEvent(
diff --git a/tools/winscope/src/viewers/common/surface_flinger_utils_component_test.ts b/tools/winscope/src/viewers/common/surface_flinger_utils_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/common/surface_flinger_utils_component_test.ts
rename to tools/winscope/src/viewers/common/surface_flinger_utils_test.ts
diff --git a/tools/winscope/src/viewers/components/coordinates_table_component_test.ts b/tools/winscope/src/viewers/components/coordinates_table_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/coordinates_table_component_test.ts
rename to tools/winscope/src/viewers/components/coordinates_table_test.ts
diff --git a/tools/winscope/src/viewers/components/hierarchy_component_test.ts b/tools/winscope/src/viewers/components/hierarchy_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/hierarchy_component_test.ts
rename to tools/winscope/src/viewers/components/hierarchy_test.ts
diff --git a/tools/winscope/src/viewers/components/ime_additional_properties_component_test.ts b/tools/winscope/src/viewers/components/ime_additional_properties_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/ime_additional_properties_component_test.ts
rename to tools/winscope/src/viewers/components/ime_additional_properties_test.ts
diff --git a/tools/winscope/src/viewers/components/properties_table_component_test.ts b/tools/winscope/src/viewers/components/properties_table_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/properties_table_component_test.ts
rename to tools/winscope/src/viewers/components/properties_table_test.ts
diff --git a/tools/winscope/src/viewers/components/properties_component_test.ts b/tools/winscope/src/viewers/components/properties_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/properties_component_test.ts
rename to tools/winscope/src/viewers/components/properties_test.ts
diff --git a/tools/winscope/src/viewers/components/rects/rects_component_test.ts b/tools/winscope/src/viewers/components/rects/rects_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/rects/rects_component_test.ts
rename to tools/winscope/src/viewers/components/rects/rects_test.ts
diff --git a/tools/winscope/src/viewers/components/surface_flinger_property_groups_component_test.ts b/tools/winscope/src/viewers/components/surface_flinger_property_groups_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/surface_flinger_property_groups_component_test.ts
rename to tools/winscope/src/viewers/components/surface_flinger_property_groups_test.ts
diff --git a/tools/winscope/src/viewers/components/transform_matrix_component_test.ts b/tools/winscope/src/viewers/components/transform_matrix_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/transform_matrix_component_test.ts
rename to tools/winscope/src/viewers/components/transform_matrix_test.ts
diff --git a/tools/winscope/src/viewers/components/tree_node_data_view_component_test.ts b/tools/winscope/src/viewers/components/tree_node_data_view_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/tree_node_data_view_component_test.ts
rename to tools/winscope/src/viewers/components/tree_node_data_view_test.ts
diff --git a/tools/winscope/src/viewers/components/tree_node_properties_data_view_component_test.ts b/tools/winscope/src/viewers/components/tree_node_properties_data_view_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/tree_node_properties_data_view_component_test.ts
rename to tools/winscope/src/viewers/components/tree_node_properties_data_view_test.ts
diff --git a/tools/winscope/src/viewers/components/tree_node_component_test.ts b/tools/winscope/src/viewers/components/tree_node_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/tree_node_component_test.ts
rename to tools/winscope/src/viewers/components/tree_node_test.ts
diff --git a/tools/winscope/src/viewers/components/tree_component_test.ts b/tools/winscope/src/viewers/components/tree_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/tree_component_test.ts
rename to tools/winscope/src/viewers/components/tree_test.ts
diff --git a/tools/winscope/src/viewers/components/viewer_input_method_component_test.ts b/tools/winscope/src/viewers/components/viewer_input_method_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/components/viewer_input_method_component_test.ts
rename to tools/winscope/src/viewers/components/viewer_input_method_test.ts
diff --git a/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component_test.ts b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component_test.ts
rename to tools/winscope/src/viewers/viewer_protolog/viewer_protolog_test.ts
diff --git a/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_component_test.ts b/tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_component_test.ts
rename to tools/winscope/src/viewers/viewer_screen_recording/viewer_screen_recording_test.ts
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_component_test.ts b/tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_component_test.ts
rename to tools/winscope/src/viewers/viewer_surface_flinger/viewer_surface_flinger_test.ts
diff --git a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts
rename to tools/winscope/src/viewers/viewer_transactions/viewer_transactions_test.ts
diff --git a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_test.ts
similarity index 97%
rename from tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
rename to tools/winscope/src/viewers/viewer_transitions/viewer_transitions_test.ts
index 26848da..fa27224 100644
--- a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_test.ts
@@ -32,7 +32,7 @@
 import {ParserTransitionsShell} from 'parsers/parser_transitions_shell';
 import {ParserTransitionsWm} from 'parsers/parser_transitions_wm';
 import {TracesParserTransitions} from 'parsers/traces_parser_transitions';
-import {KarmaTestUtils} from 'test/unit/karma_utils';
+import {UnitTestUtils} from 'test/unit/utils';
 import {Trace} from 'trace/trace';
 import {Traces} from 'trace/traces';
 import {TraceFile} from 'trace/trace_file';
@@ -127,12 +127,10 @@
 
   it('updates tree view on TracePositionUpdate event', async () => {
     const wmTransFile = new TraceFile(
-      await KarmaTestUtils.getFixtureFile(
-        'traces/elapsed_and_real_timestamp/wm_transition_trace.pb'
-      )
+      await UnitTestUtils.getFixtureFile('traces/elapsed_and_real_timestamp/wm_transition_trace.pb')
     );
     const shellTransFile = new TraceFile(
-      await KarmaTestUtils.getFixtureFile(
+      await UnitTestUtils.getFixtureFile(
         'traces/elapsed_and_real_timestamp/shell_transition_trace.pb'
       )
     );
diff --git a/tools/winscope/src/viewers/viewer_view_capture/viewer_view_capture_component_test.ts b/tools/winscope/src/viewers/viewer_view_capture/viewer_view_capture_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/viewer_view_capture/viewer_view_capture_component_test.ts
rename to tools/winscope/src/viewers/viewer_view_capture/viewer_view_capture_test.ts
diff --git a/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_component_test.ts b/tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_test.ts
similarity index 100%
rename from tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_component_test.ts
rename to tools/winscope/src/viewers/viewer_window_manager/viewer_window_manager_test.ts
diff --git a/tools/winscope/webpack.config.unit_test.js b/tools/winscope/webpack.config.unit_test.js
deleted file mode 100644
index 15b4ed4..0000000
--- a/tools/winscope/webpack.config.unit_test.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-const path = require('path');
-const glob = require('glob');
-
-const config = require('./webpack.config.common');
-
-config['mode'] = 'development';
-
-const allTestFiles = [...glob.sync('./src/**/*_test.js'), ...glob.sync('./src/**/*_test.ts')];
-const unitTestFiles = allTestFiles
-  .filter((file) => !file.match('.*_component_test\\.(js|ts)$'))
-  .filter((file) => !file.match('.*e2e.*'));
-config['entry'] = {
-  tests: unitTestFiles,
-};
-
-config['output'] = {
-  path: path.resolve(__dirname, 'dist/unit_test'),
-  filename: 'bundle.js',
-};
-
-config['target'] = 'node';
-config['node'] = {
-  __dirname: false,
-};
-
-module.exports = config;