This is an automated email from the ASF dual-hosted git repository.

tbonelee pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/master by this push:
     new c8c0d3d255 [ZEPPELIN-6685] Resolve the app's imports in the shell unit 
test setup
c8c0d3d255 is described below

commit c8c0d3d255f7edf017d312fe05d6d26ddd2deaa1
Author: YONGJAE LEE (이용재) <[email protected]>
AuthorDate: Fri Sep 4 23:42:58 2026 +0900

    [ZEPPELIN-6685] Resolve the app's imports in the shell unit test setup
    
    ### What is this PR for?
    
    `vitest.shell.config.mts` declares no `resolve.alias`, and Vite does not 
read the `paths` block in `tsconfig.base.json`, so a spec cannot reach 
application source:
    
    ```
    Failed to resolve import "<at>zeppelin/interfaces" from 
"src/app/services/array-ordering.service.ts"
    ```
    
    The failure is in the source, not the spec: most of `src/` imports 
`<at>zeppelin/*` itself, so a relative import does not avoid it.
    
    `monaco-editor` does not resolve either. It publishes no `main` and no 
`exports` 
([monaco-editor#4848](https://github.com/microsoft/monaco-editor/issues/4848)); 
`module` names `editor.main`, which boots the full editor and fails under 
jsdom. Eleven files import it, two behind the `<at>zeppelin/services` barrel.
    
    This adds the alias block, a spec that exercises it, and a note in 
`AGENTS.md`. Two entries differ from `tsconfig.base.json`:
    
    * `<at>zeppelin/sdk` and `<at>zeppelin/visualization` resolve to library 
source, not `dist/`, so a unit run does not wait on a build. 
`projects/zeppelin-react/vitest.config.mts` already does this for the SDK.
    * `monaco-editor` resolves to `esm/vs/editor/editor.api.js`.
    
    ### What type of PR is it?
    
    Improvement
    
    ### Todos
    
    None
    
    ### What is the Jira issue?
    
    ZEPPELIN-6685
    
    ### How should this be tested?
    
    `npm run test:shell` goes from 29 tests to 37. The new spec covers 
`ArrayOrderingService`: trash folder last, blank title falls back to `Note 
<id>`, folders before notes, same-kind nodes by display name.
    
    It imports through the `<at>zeppelin/services` barrel so the chain reaches 
monaco and `<at>zeppelin/sdk`. Pointing any of `monaco-editor`, 
`<at>zeppelin/sdk` or the catch-all at a nonexistent path fails it. Nothing 
imports the `sdk/*` or `visualization/*` subpaths today, so those two are 
uncovered.
    
    `ng build --configuration production` and `ng lint` both pass unchanged: 
only Vitest reads the config, and `src/tsconfig.json` excludes specs from the 
build.
    
    ### Screenshots (if appropriate)
    
    N/A
    
    ### Questions:
    
    * Does the license files need to update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    
    Closes #5439 from voidmatcha/vitest-shell-alias.
    
    Signed-off-by: ChanHo Lee <[email protected]>
---
 zeppelin-web-angular/AGENTS.md                     |  4 ++
 .../app/services/array-ordering.service.spec.ts    | 77 ++++++++++++++++++++++
 zeppelin-web-angular/vitest.shell.config.mts       | 33 ++++++++++
 3 files changed, 114 insertions(+)

diff --git a/zeppelin-web-angular/AGENTS.md b/zeppelin-web-angular/AGENTS.md
index ddec79bf2b..92b08f597a 100644
--- a/zeppelin-web-angular/AGENTS.md
+++ b/zeppelin-web-angular/AGENTS.md
@@ -84,6 +84,10 @@ A spec with no assertion, or one whose assertion sits inside 
an `if`, passes by
 
 The e2e suite gets the same protection from `eslint-plugin-playwright`.
 
+## monaco-editor and path aliases in specs
+
+`vitest.shell.config.mts` mirrors the `paths` block in `tsconfig.base.json`, 
which Vite does not read; add an alias there when you add a path. Eleven files 
under `src/` import `monaco-editor`, two behind the `@zeppelin/services` 
barrel, so a spec that reaches the editor or notebook area loads it: a few 
seconds on first import and a `marked.umd.js.map` sourcemap warning, both 
monaco's, not ours. Mock it with `vi.mock('monaco-editor', ...)` when the spec 
only needs to assert the editor was [...]
+
 ## Determinism
 
 No clock, no randomness, no network. A spec that reads `Date.now()` or fetches 
will eventually fail for reasons unrelated to the code under test.
diff --git 
a/zeppelin-web-angular/src/app/services/array-ordering.service.spec.ts 
b/zeppelin-web-angular/src/app/services/array-ordering.service.spec.ts
new file mode 100644
index 0000000000..0508814f40
--- /dev/null
+++ b/zeppelin-web-angular/src/app/services/array-ordering.service.spec.ts
@@ -0,0 +1,77 @@
+/*
+ * 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 { beforeEach, describe, expect, it } from 'vitest';
+import { NodeItem } from '@zeppelin/interfaces';
+// Through the barrel, not the neighbouring file:
+// that is what walks the alias chain to monaco and @zeppelin/sdk,
+// so this spec fails if either stops resolving.
+import { ArrayOrderingService } from '@zeppelin/services';
+
+const TRASH_FOLDER_ID = '~Trash';
+
+const note = (id: string, title: string, children?: NodeItem[]): NodeItem => 
({ id, title, children }) as NodeItem;
+
+describe('ArrayOrderingService', () => {
+  let service: ArrayOrderingService;
+
+  beforeEach(() => {
+    service = new ArrayOrderingService(TRASH_FOLDER_ID);
+  });
+
+  describe('getNoteName', () => {
+    it('returns the title when it has one', () => {
+      expect(service.getNoteName(note('a1', 'My note'))).toBe('My note');
+    });
+
+    it('falls back to the id when the title is blank', () => {
+      expect(service.getNoteName(note('a1', '   '))).toBe('Note a1');
+    });
+  });
+
+  describe('noteListOrdering', () => {
+    it('sorts the trash folder last by returning the highest code point', () 
=> {
+      expect(service.noteListOrdering(note(TRASH_FOLDER_ID, 
'Trash'))).toBe('�');
+    });
+
+    it('orders every other node by its display name', () => {
+      expect(service.noteListOrdering(note('a1', 'My note'))).toBe('My note');
+    });
+  });
+
+  describe('noteComparator', () => {
+    it('puts the trash folder after anything else, whichever side it is on', 
() => {
+      const trash = note(TRASH_FOLDER_ID, 'Trash');
+      const other = note('a1', 'My note');
+
+      expect(service.noteComparator(trash, other)).toBe(1);
+      expect(service.noteComparator(other, trash)).toBe(-1);
+    });
+
+    it('puts folders before notes', () => {
+      const folder = note('f1', 'Folder', []);
+      const leaf = note('a1', 'Note');
+
+      expect(service.noteComparator(leaf, folder)).toBe(1);
+      expect(service.noteComparator(folder, leaf)).toBe(-1);
+    });
+
+    it('compares two nodes of the same kind by display name', () => {
+      expect(service.noteComparator(note('a1', 'Alpha'), note('a2', 
'Beta'))).toBeLessThan(0);
+    });
+
+    it('uses the id fallback when a title is blank', () => {
+      // 'Note a1' sorts before 'Zebra', which the raw empty title would not.
+      expect(service.noteComparator(note('a1', ''), note('a2', 
'Zebra'))).toBeLessThan(0);
+    });
+  });
+});
diff --git a/zeppelin-web-angular/vitest.shell.config.mts 
b/zeppelin-web-angular/vitest.shell.config.mts
index da9eab60a6..c6b35960aa 100644
--- a/zeppelin-web-angular/vitest.shell.config.mts
+++ b/zeppelin-web-angular/vitest.shell.config.mts
@@ -11,9 +11,42 @@
  */
 
 // vite is pinned in package.json: the React remote keeps its own lockfile and 
drifted to a different minor.
+import { fileURLToPath } from 'node:url';
 import { defineConfig } from 'vitest/config';
 
 export default defineConfig({
+  // Mirrors the `paths` block in tsconfig.base.json, which Vite does not read.
+  // The two library aliases resolve to source, not `dist/`, so a unit run 
does not wait on a build.
+  resolve: {
+    alias: [
+      // monaco-editor ships no `main` and no `exports` 
(microsoft/monaco-editor#4848).
+      // `module` names editor.main, which boots the full editor and dies in 
jsdom.
+      // 0.55 restores resolution but still points there, so revisit rather 
than drop.
+      {
+        find: /^monaco-editor$/,
+        replacement: fileURLToPath(new 
URL('./node_modules/monaco-editor/esm/vs/editor/editor.api.js', 
import.meta.url))
+      },
+      { find: /^@zeppelin\/sdk$/, replacement: fileURLToPath(new 
URL('./projects/zeppelin-sdk/src', import.meta.url)) },
+      {
+        find: /^@zeppelin\/sdk\/(.*)$/,
+        replacement: `${fileURLToPath(new URL('./projects/zeppelin-sdk/src', 
import.meta.url))}/$1`
+      },
+      {
+        find: /^@zeppelin\/visualization$/,
+        replacement: fileURLToPath(new 
URL('./projects/zeppelin-visualization/src', import.meta.url))
+      },
+      {
+        find: /^@zeppelin\/visualization\/(.*)$/,
+        replacement: `${fileURLToPath(new 
URL('./projects/zeppelin-visualization/src', import.meta.url))}/$1`
+      },
+      // `@zeppelin/*` falls back to src/environments in tsconfig; Vite 
aliases do not.
+      {
+        find: /^@zeppelin\/environment$/,
+        replacement: fileURLToPath(new 
URL('./src/environments/environment.ts', import.meta.url))
+      },
+      { find: /^@zeppelin\/(.*)$/, replacement: `${fileURLToPath(new 
URL('./src/app', import.meta.url))}/$1` }
+    ]
+  },
   // oxc does not apply the decorator options from tsconfig.base.json to specs,
   // which src/tsconfig.json excludes. Undeclared, a decorated spec fails to
   // parse with "Invalid or unexpected token".

Reply via email to