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');
});