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

voidmatcha 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 e9d0b01b95 [ZEPPELIN-6566] Run frontend unit checks in CI
e9d0b01b95 is described below

commit e9d0b01b957605957e90dcf85fbc6192c29d6518
Author: κΉ€μ˜ˆλ‚˜ <[email protected]>
AuthorDate: Wed Oct 7 12:20:21 2026 +0900

    [ZEPPELIN-6566] Run frontend unit checks in CI
    
    ### What is this PR for?
    
    Give the frontend unit checks named results in pull-request CI and run the 
React remote tests and typecheck through Maven.
    
    * **Scripts:**
      * `projects/zeppelin-react` gains `typecheck` (`tsc --noEmit`).
      * The frontend root gains `test:react` and `typecheck:react`. Both 
delegate with `npm --prefix projects/zeppelin-react`, so the React project's 
working directory is unchanged.
    * **Maven:** the `test` phase gains two executions, `npm test react` and 
`npm typecheck react`. A failure names the execution in the Maven log, for 
example `Failed to execute goal ... npm (npm typecheck react)`.
    * **Workflow:** `frontend.yml` gains three steps on the anonymous leg of 
`run-playwright-e2e-tests`: `Run Angular shell unit tests`, `Run React unit 
tests` and `Typecheck React`.
      * They call the same root scripts, so the local, Maven and workflow entry 
points run one implementation.
      * The auth leg still runs the Maven-owned checks inside `mvnw verify 
-Pweb-e2e`, without a duplicate named result.
    * **TS2802 fix:** `exportFile.spec.ts` spread a `Uint8Array`, which fails 
`tsc --noEmit` under the React project's ES5 target. It now uses `Array.from`. 
This was the only typecheck error on `master` (`943a3fb8d`).
    * **`fail-fast: false` on the e2e matrix:**
      * When I first ran the deliberate typecheck failure on my fork, the auth 
leg's Maven `npm typecheck react` execution failed first. Matrix fail-fast then 
cancelled the anonymous leg during `Install application`, so `Typecheck React` 
never ran.
      * The failure only showed up under the e2e job name, which is what this 
issue is meant to prevent.
      * With `fail-fast: false`, both legs report 
([run](https://github.com/kimyenac/zeppelin/actions/runs/37421560253)). 
`core.yml` already uses `fail-fast: false` for its matrices.
    * **Docs:** `AGENTS.md` and `projects/zeppelin-react/AGENTS.md` describe 
the new commands and CI steps. The parts that are still true are kept:
      * the checks without a named step still run inside `verify -Pweb-e2e` on 
both legs and surface under the e2e job name;
      * React lint still runs only there;
      * the npm-audit remediation workflow also runs `npm test`.
    
    Commits, continued from the `ZEPPELIN-6566-shell-react-ci` branch in 
`voidmatcha/zeppelin` with the author's agreement:
    1. `7c709e5ac`, the original commit unchanged: scripts, Maven executions, 
named steps, the TS2802 fix and the first docs update.
    2. `038784611`: `fail-fast: false` on the e2e matrix.
    3. `9adb65413`: the docs corrections above.
    
    The verification below covers the combined result.
    
    Cost: on the anonymous leg the shell tests, React tests and React typecheck 
now run twice, once as named steps and again in the Maven `test` phase inside 
`verify`. Locally that is about 16 seconds in total: shell 8.2s, React 6.7s, 
typecheck 1.5s.
    
    ### What type of PR is it?
    Improvement
    
    ### Todos
    * [x] Add `typecheck` to the React project, and `test:react` / 
`typecheck:react` to the frontend root
    * [x] Add Maven `npm test react` and `npm typecheck react` executions
    * [x] Add named shell test, React test and React typecheck steps on the 
anonymous leg
    * [x] Fix the existing TS2802 typecheck failure
    * [x] Keep the anonymous leg from being cancelled before its named steps 
report
    * [x] Demonstrate the deliberate failures and revert them
    
    ### What is the Jira issue?
    [ZEPPELIN-6566](https://issues.apache.org/jira/browse/ZEPPELIN-6566)
    
    ### How should this be tested?
    
    ```bash
    cd zeppelin-web-angular
    npm run test:react
    npm run typecheck:react
    cd ..
    ./mvnw test -pl zeppelin-web-angular
    ```
    
    **Same pass count from every entry point.** The React suite runs 15 files / 
105 tests from each of:
    * `npm test` in `projects/zeppelin-react`
    * `npm run test:react` at the frontend root
    * the Maven `npm test react` execution
    
    The shell suite runs 211 tests. `./mvnw test -pl zeppelin-web-angular` 
reports BUILD SUCCESS.
    
    **Deliberate failures (all reverted).** Each one was written to pass lint, 
so it fails in the targeted check and not earlier. The fork runs used throwaway 
branches, now deleted; the run pages remain.
    
    | Deliberate failure | Local | Fork CI (anonymous leg) |
    | --- | --- | --- |
    | failing shell spec | `npm run test:shell` fails (1 failed / 211 passed) | 
`Run Angular shell unit tests` fails 
([run](https://github.com/kimyenac/zeppelin/actions/runs/37415025910)) |
    | failing React spec | `npm run test:react` fails; Maven fails at `(npm 
test react)` | `Run React unit tests` fails 
([run](https://github.com/kimyenac/zeppelin/actions/runs/37415031039)) |
    | TypeScript error in React `src` | `npm run typecheck:react` fails; Maven 
fails at `(npm typecheck react)` | `Typecheck React` fails, and the auth leg 
fails at Maven `(npm typecheck react)` 
([run](https://github.com/kimyenac/zeppelin/actions/runs/37421560253)) |
    
    For the TypeScript error, the React webpack build uses `ts-loader` with 
`transpileOnly: true`. So the error does not fail `Install application` first, 
and it surfaces at the named step.
    
    ### Screenshots (if appropriate)
    
    N/A.
    
    ### Questions:
    * Does the license files need to update? No.
    * Is there breaking changes for older versions? No. Existing scripts and 
executions are unchanged.
    * Does this needs documentation? The two `AGENTS.md` files are updated.
    
    πŸ€– Generated with [Claude Code](https://claude.com/claude-code)
    
    
    Closes #5548 from kimyenac/ZEPPELIN-6566.
    
    Signed-off-by: YONGJAE LEE <[email protected]>
---
 .github/workflows/frontend.yml                     | 15 +++++++++++++++
 zeppelin-web-angular/AGENTS.md                     | 10 ++++++----
 zeppelin-web-angular/package.json                  |  2 ++
 zeppelin-web-angular/pom.xml                       | 22 ++++++++++++++++++++++
 .../projects/zeppelin-react/AGENTS.md              |  3 ++-
 .../projects/zeppelin-react/eslint.config.js       |  1 +
 .../projects/zeppelin-react/package.json           |  3 ++-
 .../zeppelin-react/src/utils/exportFile.spec.ts    |  2 +-
 8 files changed, 51 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml
index 3e6f4ba206..37a4d1a438 100644
--- a/.github/workflows/frontend.yml
+++ b/.github/workflows/frontend.yml
@@ -50,6 +50,9 @@ jobs:
       # Use VFS storage instead of Git to avoid Git-related issues in CI
       ZEPPELIN_NOTEBOOK_STORAGE: 
org.apache.zeppelin.notebook.repo.VFSNotebookRepo
     strategy:
+      # Keep both legs running so a Maven-owned failure on the auth leg cannot 
cancel the anonymous leg
+      # before its named unit test and typecheck steps report.
+      fail-fast: false
       matrix:
         mode: [anonymous, auth]
         python: [ 3.9 ]
@@ -94,6 +97,18 @@ jobs:
           channel-priority: strict
       - name: Install application
         run: ./mvnw clean install -DskipTests -am -pl 
python,zeppelin-jupyter-interpreter,zeppelin-web,zeppelin-web-angular 
-Pweb-classic ${MAVEN_ARGS}
+      - name: Run Angular shell unit tests
+        if: matrix.mode == 'anonymous'
+        working-directory: zeppelin-web-angular
+        run: ./node/npm run test:shell
+      - name: Run React unit tests
+        if: matrix.mode == 'anonymous'
+        working-directory: zeppelin-web-angular
+        run: ./node/npm run test:react
+      - name: Typecheck React
+        if: matrix.mode == 'anonymous'
+        working-directory: zeppelin-web-angular
+        run: ./node/npm run typecheck:react
       - name: Check websocket operation contract
         if: matrix.mode == 'anonymous'
         working-directory: zeppelin-web-angular
diff --git a/zeppelin-web-angular/AGENTS.md b/zeppelin-web-angular/AGENTS.md
index 27a29f4785..9a0c81d28e 100644
--- a/zeppelin-web-angular/AGENTS.md
+++ b/zeppelin-web-angular/AGENTS.md
@@ -37,11 +37,13 @@ The repository root `AGENTS.md` asks every change to 
include unit tests. This fi
 | `npm run test:shell` | Run the unit tests for `src/`, `test/` and the two 
libraries |
 | `npm run test:shell -- --coverage` | Same, with a coverage report |
 | `npm run test:shell -- foo.spec.ts` | Run one file |
+| `npm run test:react` | Run the React remote unit tests |
+| `npm run typecheck:react` | Typecheck the React remote without emitting 
files |
 | `npm run test:notebook-core` | Run the dedicated notebook-core Node suite |
 | `npm run typecheck:notebook-core` | Check core source and specs, rebuild the 
package, and check the React type-only contract against built declarations and 
the same core source |
 | `npm run typecheck:sdk-contracts` | Check the `zeppelin-sdk` specs, 
including their [type assertions](#type-assertions) |
 
-`test:shell`, `test:notebook-core`, `typecheck:notebook-core`, and 
`typecheck:sdk-contracts` are bound to the Maven `test` phase (`pom.xml`), so a 
spec added here starts running in CI the day it merges. It does not run where 
you would expect. `frontend.yml` builds this module with `-DskipTests`, which 
frontend-maven-plugin honours by skipping `test`-phase executions, so the run 
that counts is `mvnw verify -Pweb-e2e` inside the `run-playwright-e2e-tests` 
job. A failing spec surfaces there [...]
+`test:shell`, `test:react`, `typecheck:react`, `test:notebook-core`, 
`typecheck:notebook-core`, and `typecheck:sdk-contracts` are bound to the Maven 
`test` phase (`pom.xml`), so a spec added here starts running in CI the day it 
merges. `frontend.yml` builds this module with `-DskipTests`, which 
frontend-maven-plugin honours by skipping `test`-phase executions, so all of 
them run inside `mvnw verify -Pweb-e2e` in the `run-playwright-e2e-tests` job, 
on both legs. `test:shell`, `test:react` [...]
 
 ## Where a test belongs
 
@@ -92,16 +94,16 @@ The e2e suite gets the same protection from 
`eslint-plugin-playwright`.
 
 ## Type assertions
 
-`expectTypeOf`, `assertType` and `@ts-expect-error` are erased before a spec 
runs, so Vitest passes them whatever they say. Only a `tsc` pass over the spec 
checks them, and only two spec programs have one in Maven:
+`expectTypeOf`, `assertType` and `@ts-expect-error` are erased before a spec 
runs, so Vitest passes them whatever they say. Only a `tsc` pass over the spec 
checks them, and only three spec programs have one in Maven:
 
 | Specs | Checked by |
 | --- | --- |
 | `projects/zeppelin-sdk` | `typecheck:sdk-contracts` |
 | `projects/zeppelin-notebook-core`, `test/notebook-core` | 
`typecheck:notebook-core` |
+| `projects/zeppelin-react` | `typecheck:react` |
 | `src/`, the rest of `test/`, `projects/zeppelin-visualization` | nothing yet 
|
-| `projects/zeppelin-react` | nothing yet 
([ZEPPELIN-6566](https://issues.apache.org/jira/browse/ZEPPELIN-6566)) |
 
-Lint encodes the table: `vitest/expect-expect` accepts a type assertion as a 
test's only assertion in the first two rows and rejects it elsewhere. A type 
assertion in an unchecked spec, even beside an `expect`, cannot fail. When a 
program gains a `tsc` pass in Maven, add its glob to the `settings: { vitest: { 
typecheck: true } }` block in `eslint.config.js`, or set the same in 
`projects/zeppelin-react/eslint.config.js`.
+Lint encodes the table: `vitest/expect-expect` accepts a type assertion as a 
test's only assertion in the first three rows and rejects it elsewhere. A type 
assertion in an unchecked spec, even beside an `expect`, cannot fail. When a 
program gains a `tsc` pass in Maven, add its glob to the `settings: { vitest: { 
typecheck: true } }` block in `eslint.config.js`, or set the same in 
`projects/zeppelin-react/eslint.config.js`.
 
 - Put type assertions in an ordinary `.spec.ts`, never a separate type-test 
file or directory. One SDK interface file declares many unrelated types, so a 
type contract spec sits beside that file and is named for the contract it pins: 
`notebook-wire-fields.spec.ts`, `completion-item.spec.ts`.
 - To show a value is accepted, pass it to `assertType<T>(...)`. A typed 
variable then needs a use, and a runtime `expect` on a literal only restates 
the literal.
diff --git a/zeppelin-web-angular/package.json 
b/zeppelin-web-angular/package.json
index 4288084e8a..c2f926e60c 100644
--- a/zeppelin-web-angular/package.json
+++ b/zeppelin-web-angular/package.json
@@ -30,6 +30,8 @@
     "test:notebook-route-boundary": "node --test 
e2e/core-contract/notebook-route-boundary.test.mjs",
     "test:notebook-core": "vitest run --config 
vitest.notebook-core.config.mts",
     "test:shell": "vitest run --config vitest.shell.config.mts",
+    "test:react": "npm --prefix projects/zeppelin-react test",
+    "typecheck:react": "npm --prefix projects/zeppelin-react run typecheck",
     "test:eslint-rules": "node --test eslint-rules/*.test.js",
     "e2e": "playwright test",
     "check:core-contract-fixtures": "node --test 
e2e/core-contract/notebook-transport-fixture.test.mjs 
e2e/core-contract/playwright-runner.test.mjs 
e2e/core-contract/reject-notebook-core-runtime-plugin.test.mjs",
diff --git a/zeppelin-web-angular/pom.xml b/zeppelin-web-angular/pom.xml
index ab25e21de5..857de6fbf6 100644
--- a/zeppelin-web-angular/pom.xml
+++ b/zeppelin-web-angular/pom.xml
@@ -131,6 +131,28 @@
             </configuration>
           </execution>
 
+          <execution>
+            <id>npm test react</id>
+            <goals>
+              <goal>npm</goal>
+            </goals>
+            <phase>test</phase>
+            <configuration>
+              <arguments>run test:react</arguments>
+            </configuration>
+          </execution>
+
+          <execution>
+            <id>npm typecheck react</id>
+            <goals>
+              <goal>npm</goal>
+            </goals>
+            <phase>test</phase>
+            <configuration>
+              <arguments>run typecheck:react</arguments>
+            </configuration>
+          </execution>
+
           <execution>
             <id>npm check core contract fixtures</id>
             <goals>
diff --git a/zeppelin-web-angular/projects/zeppelin-react/AGENTS.md 
b/zeppelin-web-angular/projects/zeppelin-react/AGENTS.md
index 8f01d9dabc..e9b2fa8f03 100644
--- a/zeppelin-web-angular/projects/zeppelin-react/AGENTS.md
+++ b/zeppelin-web-angular/projects/zeppelin-react/AGENTS.md
@@ -28,8 +28,9 @@ Unit test conventions for the React remote, covering only 
what differs from the
 | `npm test` | Run once |
 | `npm run test:watch` | Re-run on change |
 | `npm test -- --coverage` | With a coverage report |
+| `npm run typecheck` | Typecheck without emitting files |
 
-**`npm test` never runs in pull-request CI.** The build does, through 
`build:react` at the Maven `generate-resources` phase. The lint does too, 
through `lint:react`, but only inside the `run-playwright-e2e-tests` job, 
because `npm lint` is bound to the `test` phase and `frontend.yml` builds this 
module with `-DskipTests`. `npm audit` has its own job. The test suite is 
invoked from the npm-audit remediation workflow, not from the normal PR path, 
and connecting it is [ZEPPELIN-6566](https: [...]
+`npm test` and `npm run typecheck` run in pull-request CI as named steps on 
the anonymous frontend leg, through the frontend root scripts `test:react` and 
`typecheck:react`. Both are also bound to the Maven `test` phase, so `mvnw 
verify -Pweb-e2e` runs them again inside the `run-playwright-e2e-tests` job. 
The build runs through `build:react` at the Maven `generate-resources` phase. 
The lint runs through `lint:react`, but only inside the 
`run-playwright-e2e-tests` job, because `npm lint`  [...]
 
 Specs are `Foo.spec.tsx` beside `Foo.tsx`, picked up by this package's own 
`vitest.config.mts`. `@testing-library/react` is available.
 
diff --git a/zeppelin-web-angular/projects/zeppelin-react/eslint.config.js 
b/zeppelin-web-angular/projects/zeppelin-react/eslint.config.js
index c7f5de412d..71e4f5142c 100644
--- a/zeppelin-web-angular/projects/zeppelin-react/eslint.config.js
+++ b/zeppelin-web-angular/projects/zeppelin-react/eslint.config.js
@@ -95,6 +95,7 @@ module.exports = tseslint.config(
     // Catch specs that cannot fail, as eslint-plugin-playwright does for e2e.
     files: ['src/**/*.spec.{ts,tsx}'],
     plugins: { vitest },
+    settings: { vitest: { typecheck: true } },
     rules: {
       'vitest/expect-expect': 'error',
       'vitest/no-conditional-expect': 'error',
diff --git a/zeppelin-web-angular/projects/zeppelin-react/package.json 
b/zeppelin-web-angular/projects/zeppelin-react/package.json
index b9c5edc537..119d40a353 100644
--- a/zeppelin-web-angular/projects/zeppelin-react/package.json
+++ b/zeppelin-web-angular/projects/zeppelin-react/package.json
@@ -11,7 +11,8 @@
     "lint": "eslint 'src/**/*.{ts,tsx}'",
     "lint:fix": "eslint 'src/**/*.{ts,tsx}' --fix",
     "test": "vitest run",
-    "test:watch": "vitest"
+    "test:watch": "vitest",
+    "typecheck": "tsc --noEmit"
   },
   "dependencies": {
     "@ant-design/icons": "5.4.0",
diff --git 
a/zeppelin-web-angular/projects/zeppelin-react/src/utils/exportFile.spec.ts 
b/zeppelin-web-angular/projects/zeppelin-react/src/utils/exportFile.spec.ts
index d4ead52faa..09f3424d53 100644
--- a/zeppelin-web-angular/projects/zeppelin-react/src/utils/exportFile.spec.ts
+++ b/zeppelin-web-angular/projects/zeppelin-react/src/utils/exportFile.spec.ts
@@ -37,7 +37,7 @@ describe('exportFile', () => {
     await exportFile({ columnNames: ['name', 'city'], rows: [['alice', 'μ„œμšΈ']] 
}, 'csv');
 
     const bytes = await savedBytes();
-    expect([...bytes.subarray(0, 3)]).toEqual(UTF8_BOM);
+    expect(Array.from(bytes.subarray(0, 3))).toEqual(UTF8_BOM);
     expect(new 
TextDecoder().decode(bytes.subarray(3))).toBe('name,city\nalice,μ„œμšΈ');
     expect(saveAs.mock.calls[0][1]).toBe('export.csv');
   });

Reply via email to