This is an automated email from the ASF dual-hosted git repository.
stigahuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git
The following commit(s) were added to refs/heads/master by this push:
new 7756e5bc3 IMPALA-13473: Add support for JS code analysis and linting
with ESLint
7756e5bc3 is described below
commit 7756e5bc32e0d50fc3007ff2c647644f24f68047
Author: Surya Hebbar <[email protected]>
AuthorDate: Thu Mar 20 14:45:26 2025 +0530
IMPALA-13473: Add support for JS code analysis and linting with ESLint
This patch adds support for JS code analysis and linting to webUI
scripts using ESLint.
Support to enforce code style and quality is partcularly beneficial,
as the codebase for client-side scripts is consistently growing.
This has been implemented to work alongside other code style enforcement
rules present within 'critique-gerrit-review.py', which runs on the
existing jenkins job 'gerrit-auto-critic', to produce gerrit comments.
In the case of webUI scripts, ESLint's code analysis and linting checks
are performed to produce these comments.
As a shared NodeJS installation can be used for JS tests as well as
linting, a seperate common script "bin/nodejs/setup_nodejs.sh"
has been added for assiting with the NodeJS installation.
To ensure quicker run times for the jenkins job, NodeJS tarball is
cached within "${HOME}/.cache" directory, after the initial installation.
ESLint's packages and dependencies have been made to be cached
using NPM's own package management and are also cached locally.
NodeJS and ESLint dependencies are retrieved and executed, only if
there are any changes within ".js" files within the patchset,
and run with minimal overhead.
After analysis, comments are generated for all the violations according
to the specified rules.
A custom formatter has been added to extract, format and filter the
violations in JSON form.
These generated code style violations are formatted into the required
JSON form according to gerrit's REST API, similar to comments generated
by flake8. These are then posted to gerrit as comments
on the respective patchset from jenkins over SSH.
The following code style and quality rules have been added using ESLint.
- Disallow unused variables
- Enforce strict equality (=== and !==)
- Require curly braces for all control statements (if, while, etc.)
- Enforce semicolons at the end of statements
- Enforce double quotes for strings
- Set maximum line length to 90
- Disallow `var`, use `let` or `const`
- Prefer `const` where possible
- Disallow multiple empty lines
- Enforce spacing around infix operators (eg. +, =)
- Disallow the use of undeclared variables
- Require parentheses around arrow function arguments
- Require a space before blocks
- Enforce consistent spacing inside braces
- Disallow shadowing variables declared in the outer scope
- Disallow constant conditions in if statements, loops, etc
- Disallow unnecessary parentheses in expressions
- Disallow duplicate arguments in function definitions
- Disallow duplicate keys in object literals
- Disallow unreachable code after return, throw, continue, etc
- Disallow reassigning function parameters
- Require functions to always consistently return or not return at all
- Enforce consistent use of dot notation wherever possible
- Disallow multiple empty lines
- Enforce spacing around the colon in object literal properties
- Disallow optional chaining, where undefined values are not allowed
The required linting packages have been added as dependencies in the
"www/scripts" directory.
All the test scripts and related dependencies have been moved to -
$IMPALA_HOME/tests/webui/js_tests.
All the custom ESLint formatter scripts and related dependencies
have been moved to -
$IMPALA_HOME/tests/webui/linting.
A combination of NodeJS's 'prefix' argument and NODE_PATH environmental
variable is being used to seperate the dependencies and webUI scripts.
To support running the tests from a remote directory(i.e. tests/webui),
by modifying the required base paths.
The JS scripts need to be updated according to these linting rules,
as per IMPALA-13986.
Change-Id: Ieb3d0a9221738e2ac6fefd60087eaeee4366e33f
Reviewed-on: http://gerrit.cloudera.org:8080/21970
Reviewed-by: Riza Suminto <[email protected]>
Tested-by: Impala Public Jenkins <[email protected]>
---
bin/jenkins/critique-gerrit-review.py | 41 ++++++++++-
.../run-js-tests.sh => bin/nodejs/setup_nodejs.sh | 56 +++++++--------
bin/run-all-tests.sh | 2 +-
tests/run-tests.py | 2 +-
.../tests => tests/webui/js_tests}/.gitignore | 0
.../tests => tests/webui/js_tests}/package.json | 4 +-
.../js_tests}/queries/profileParseWorker.test.js | 2 +-
.../js_tests}/query_timeline/chart_commons.test.js | 2 +-
.../query_timeline/fragment_diagram.test.js | 2 +-
.../fragment_metrics_diagram.test.js | 2 +-
.../host_utilization_diagram.test.js | 2 +-
tests/webui/linting/.gitignore | 2 +
tests/webui/linting/eslint.config.js | 84 ++++++++++++++++++++++
.../eslint/eslint_custom_output_formatter.js | 30 ++++++++
tests/webui/linting/package.json | 7 ++
tests/webui/run-js-tests.sh | 37 ++++++++++
www/scripts/query_timeline/package.json | 5 +-
17 files changed, 238 insertions(+), 42 deletions(-)
diff --git a/bin/jenkins/critique-gerrit-review.py
b/bin/jenkins/critique-gerrit-review.py
index 56aaf1b6b..c0620ec33 100755
--- a/bin/jenkins/critique-gerrit-review.py
+++ b/bin/jenkins/critique-gerrit-review.py
@@ -411,6 +411,43 @@ def get_planner_tests_comments():
return comments
+def get_eslint_comments(base_revision, revision):
+ """Get comments based on JS code analysis and styling rules for
+ all scripts present in the git commit 'revision'."""
+ filenames = check_output(["git", "diff", "{0}..{1}".format(base_revision,
revision),
+ "--name-only", "--", "*.js"], universal_newlines=True)
+ filenames = filenames.strip().splitlines()
+ comments = {}
+ if (len(filenames) > 0):
+ node_env = {}
+ if (os.environ.get("IMPALA_SCRIPTS_HOME")):
+ node_env["IMPALA_HOME"] = os.environ.get("IMPALA_SCRIPTS_HOME")
+ else:
+ node_env["IMPALA_HOME"] = os.environ.get("IMPALA_HOME")
+ node_env["HOME"] = os.environ.get("HOME")
+
+ NODEJS_SETUP = os.path.join(node_env["IMPALA_HOME"],
"bin/nodejs/setup_nodejs.sh")
+ NODEJS_BIN = check_output([NODEJS_SETUP], env=node_env).decode("utf-8")
+ NODEJS_BIN = NODEJS_BIN.splitlines()[-1]
+ NODE = os.path.join(NODEJS_BIN, "node")
+ NPM = os.path.join(NODEJS_BIN, "npm")
+ LINTING_TESTS_HOME = os.path.join(node_env["IMPALA_HOME"],
"tests/webui/linting")
+ check_output([NODE, NPM, "--prefix", LINTING_TESTS_HOME, "install"])
+ filenames = [os.path.join(node_env["IMPALA_HOME"], filename) for filename
in
+ filenames]
+ try:
+ node_env["IMPALA_NODEJS"] = NODE
+ node_env["REVIEW_JS_FILES"] = ' '.join(filenames)
+ LINTING_BIN = os.path.join(LINTING_TESTS_HOME,
"node_modules/.bin/eslint")
+ LINTING_FORMATTER = os.path.join(LINTING_TESTS_HOME,
+
"eslint/eslint_custom_output_formatter.js")
+ LINTING_CONFIG = os.path.join(LINTING_TESTS_HOME, "eslint.config.js")
+ check_output([NODE, LINTING_BIN, "--config", LINTING_CONFIG, *filenames,
"-f",
+ LINTING_FORMATTER], env=node_env).decode("utf-8")
+ except Exception as e:
+ comments = json.loads(e.output)
+ return comments
+
def post_review_to_gerrit(review_input):
"""Post a review to the gerrit patchset. 'review_input' is a ReviewInput
JSON object
containing the review comments. The gerrit change and patchset are picked up
from
@@ -452,9 +489,9 @@ if __name__ == "__main__":
base_revision = args.base_revision if args.base_revision else
"{0}^".format(revision)
comments = get_flake8_comments(base_revision, revision)
merge_comments(comments, get_misc_comments(base_revision, revision,
args.dryrun))
- merge_comments(
- comments, get_catalog_compatibility_comments(base_revision, revision))
+ merge_comments(comments, get_catalog_compatibility_comments(base_revision,
revision))
merge_comments(comments, get_planner_tests_comments())
+ merge_comments(comments, get_eslint_comments(base_revision, revision))
review_input = {"comments": comments}
if len(comments) > 0:
review_input["message"] = (
diff --git a/tests/run-js-tests.sh b/bin/nodejs/setup_nodejs.sh
similarity index 51%
rename from tests/run-js-tests.sh
rename to bin/nodejs/setup_nodejs.sh
index 8e073ff60..355a90284 100755
--- a/tests/run-js-tests.sh
+++ b/bin/nodejs/setup_nodejs.sh
@@ -21,52 +21,52 @@ set -euo pipefail
. "$IMPALA_HOME/bin/report_build_error.sh"
setup_report_build_error
-: ${IMPALA_JS_TEST_LOGS_DIR:="${IMPALA_LOGS_DIR}/js_tests"}
-
-NODEJS_VERSION=v16.20.2
-# ARCH_NAME is set in bin/impala-config.sh
+: ${ARCH_NAME:=$(uname -p)}
if [[ $ARCH_NAME == aarch64 ]]; then
NODEJS_DISTRO=linux-arm64
elif [[ $ARCH_NAME == x86_64 ]]; then
NODEJS_DISTRO=linux-x64
else
echo "This script supports Intel x86_64 or ARM aarch64 CPU architectures
only." >&2
- echo "Current CPU type is reported as $ARCH_NAME" >&2
+ echo "Current CPU type is reported as '$ARCH_NAME'" >&2
# report the installation failure as a JUnit symptom
"${IMPALA_HOME}"/bin/generate_junitxml.py --phase JS_TEST \
- -- step "node.js installation" \
+ --step "node.js installation" \
--error "Unknown CPU architecture $ARCH_NAME encountered."
exit 1
fi
-NODEJS_LIB_PATH="${IMPALA_TOOLCHAIN}/node-${NODEJS_VERSION}"
-export IMPALA_NODEJS="${NODEJS_LIB_PATH}/bin/node"
-NPM="${NODEJS_LIB_PATH}/bin/npm"
-JS_TESTS_DIR="${IMPALA_HOME}/www/scripts/tests"
+: ${IMPALA_TOOLCHAIN_HOST:=native-toolchain.s3.amazonaws.com}
+: ${IMPALA_NODEJS_VERSION:=v16.20.2}
+: ${IMPALA_TOOLCHAIN:="$IMPALA_HOME/toolchain"}
-export IMPALA_JS_TEST_LOGS_DIR;
+IMPALA_NODEJS_LIB="${IMPALA_TOOLCHAIN}/node-${IMPALA_NODEJS_VERSION}"
+CACHED_NODEJS_PATH="${HOME}/.cache/impala_nodejs"
# Install nodejs locally, if not installed
-if [ -r "$IMPALA_NODEJS" ]; then
- echo "NodeJS ${NODEJS_VERSION} installation found";
-else
- echo "Fetching NodeJS ${NODEJS_VERSION}-${NODEJS_DISTRO} binaries ...";
- NODE_URL_PREFIX="https://${IMPALA_TOOLCHAIN_HOST}/mirror/nodejs"
- NODE_URL_SUFFIX="node-${NODEJS_VERSION}-${NODEJS_DISTRO}.tar.xz"
- curl "${NODE_URL_PREFIX}/${NODE_URL_SUFFIX}" -O
+if [ ! -r "${IMPALA_NODEJS_LIB}/bin/node" ]; then
+ echo "Fetching NodeJS ${IMPALA_NODEJS_VERSION}-${NODEJS_DISTRO} binaries ..."
- tar -xJf node-${NODEJS_VERSION}-${NODEJS_DISTRO}.tar.xz
+ NODE_URL_SUFFIX="node-${IMPALA_NODEJS_VERSION}-${NODEJS_DISTRO}.tar.xz"
+ CACHED_NODEJS_TARBALL="${CACHED_NODEJS_PATH}/${NODE_URL_SUFFIX}"
- mkdir -p "${NODEJS_LIB_PATH}"
+ if [ ! -r "${CACHED_NODEJS_TARBALL}" ]; then
+ NODE_URL_PREFIX="https://${IMPALA_TOOLCHAIN_HOST}/mirror/nodejs"
- mv node-${NODEJS_VERSION}-${NODEJS_DISTRO}/* -t "${NODEJS_LIB_PATH}";
+ mkdir -p "$CACHED_NODEJS_PATH"
- rm -rf node-${NODEJS_VERSION}-${NODEJS_DISTRO}.tar.xz \
- node-${NODEJS_VERSION}-${NODEJS_DISTRO}/
-fi;
+ curl "${NODE_URL_PREFIX}/${NODE_URL_SUFFIX}" -o "${CACHED_NODEJS_TARBALL}"
+ fi
+
+ tar -xJf "${CACHED_NODEJS_TARBALL}" -C ./
-# Install packages in package.json
-"$IMPALA_NODEJS" "$NPM" --prefix "${JS_TESTS_DIR}" install
+ mkdir -p "${IMPALA_NODEJS_LIB}"
+
+ mv node-${IMPALA_NODEJS_VERSION}-${NODEJS_DISTRO}/* -t "${IMPALA_NODEJS_LIB}"
+
+ rm -rf node-${IMPALA_NODEJS_VERSION}-${NODEJS_DISTRO}.tar.xz \
+ node-${IMPALA_NODEJS_VERSION}-${NODEJS_DISTRO}/
+fi;
-# Run all JEST testing suites (by default *.test.js)
-"$IMPALA_NODEJS" "$NPM" --prefix "${JS_TESTS_DIR}" test
+echo "NodeJS installation found in :"
+echo "${IMPALA_NODEJS_LIB}/bin"
\ No newline at end of file
diff --git a/bin/run-all-tests.sh b/bin/run-all-tests.sh
index 91706a034..fe7f59dd3 100755
--- a/bin/run-all-tests.sh
+++ b/bin/run-all-tests.sh
@@ -352,7 +352,7 @@ do
fi
if [[ "$JS_TEST" == true ]]; then
- if ! "${IMPALA_HOME}/tests/run-js-tests.sh"; then
+ if ! "${IMPALA_HOME}/tests/webui/run-js-tests.sh"; then
TEST_RET_CODE=1
fi
fi
diff --git a/tests/run-tests.py b/tests/run-tests.py
index df85e4511..335d88c4c 100755
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -48,7 +48,7 @@ VALID_TEST_DIRS = ['failure', 'query_test', 'stress',
'unittests', 'aux_query_te
TEST_HELPER_DIRS = ['aux_parquet_data_load', 'comparison', 'benchmark',
'custom_cluster', 'util', 'experiments', 'verifiers',
'common',
'performance', 'beeswax', 'aux_custom_cluster_tests',
- 'authorization', 'test-hive-udfs', '__pycache__']
+ 'authorization', 'test-hive-udfs', '__pycache__', 'webui']
TEST_DIR = os.path.join(os.environ['IMPALA_HOME'], 'tests')
RESULT_DIR = os.path.join(os.environ['IMPALA_EE_TEST_LOGS_DIR'], 'results')
diff --git a/www/scripts/tests/.gitignore b/tests/webui/js_tests/.gitignore
similarity index 100%
rename from www/scripts/tests/.gitignore
rename to tests/webui/js_tests/.gitignore
diff --git a/www/scripts/tests/package.json b/tests/webui/js_tests/package.json
similarity index 88%
rename from www/scripts/tests/package.json
rename to tests/webui/js_tests/package.json
index 32f981421..657b9ce2f 100644
--- a/www/scripts/tests/package.json
+++ b/tests/webui/js_tests/package.json
@@ -1,11 +1,11 @@
{
- "name": "Impala WebUI JS Tests",
+ "name": "impala-webui-js-tests",
"version": "1.0.0",
"type": "module",
"scripts": {
"test": "JEST_JUNIT_OUTPUT_DIR=\"${IMPALA_JS_TEST_LOGS_DIR}\"
\"${IMPALA_NODEJS}\" --experimental-vm-modules node_modules/jest/bin/jest.js"
},
- "devDependencies": {
+ "dependencies": {
"jest": "^29.6.4",
"jest-environment-jsdom": "^29.6.4",
"jest-junit": "^16.0.0"
diff --git a/www/scripts/tests/queries/profileParseWorker.test.js
b/tests/webui/js_tests/queries/profileParseWorker.test.js
similarity index 96%
rename from www/scripts/tests/queries/profileParseWorker.test.js
rename to tests/webui/js_tests/queries/profileParseWorker.test.js
index 2c10b26e7..e2e6d4b0d 100644
--- a/www/scripts/tests/queries/profileParseWorker.test.js
+++ b/tests/webui/js_tests/queries/profileParseWorker.test.js
@@ -26,7 +26,7 @@ describe("Test Compression Library", () => {
const exampleJSONProfileText =
readFileSync("../../../testdata/impala-profiles/"
+
"impala_profile_log_tpcds_compute_stats_extended.expected.pretty.json",
{encoding : "utf-8"});
- import("../../../pako.min.js").then((pako) => {
+ import("pako.min.js").then((pako) => {
pako = pako.default;
expect(pako.inflate(pako.deflate(exampleJSONProfileText, {level : 3}),
{to : "string"}))
.toBe(exampleJSONProfileText);
diff --git a/www/scripts/tests/query_timeline/chart_commons.test.js
b/tests/webui/js_tests/query_timeline/chart_commons.test.js
similarity index 99%
rename from www/scripts/tests/query_timeline/chart_commons.test.js
rename to tests/webui/js_tests/query_timeline/chart_commons.test.js
index a44bb13ae..367fb8fff 100644
--- a/www/scripts/tests/query_timeline/chart_commons.test.js
+++ b/tests/webui/js_tests/query_timeline/chart_commons.test.js
@@ -18,7 +18,7 @@
import {describe, test, expect} from '@jest/globals';
import {exportedForTest, generateTimesamples, clearTimeseriesValues,
mapTimeseriesCounters, aggregateProfileTimeseries} from
- '../../query_timeline/chart_commons.js';
+ 'scripts/query_timeline/chart_commons.js';
describe("Test mapTimeseriesCounters", () => {
// Test whether the method correctly searches and maps indexes of counters
based
diff --git a/www/scripts/tests/query_timeline/fragment_diagram.test.js
b/tests/webui/js_tests/query_timeline/fragment_diagram.test.js
similarity index 97%
rename from www/scripts/tests/query_timeline/fragment_diagram.test.js
rename to tests/webui/js_tests/query_timeline/fragment_diagram.test.js
index 372152ed8..35112a5fc 100644
--- a/www/scripts/tests/query_timeline/fragment_diagram.test.js
+++ b/tests/webui/js_tests/query_timeline/fragment_diagram.test.js
@@ -16,7 +16,7 @@
// under the License.
import {describe, test, expect} from '@jest/globals';
-import {exportedForTest} from "../../query_timeline/fragment_diagram.js";
+import {exportedForTest} from "scripts/query_timeline/fragment_diagram.js";
describe("Test getSvg*", () => {
// Test whether getSvg* methods correctly set attributes and return expected
elements
diff --git a/www/scripts/tests/query_timeline/fragment_metrics_diagram.test.js
b/tests/webui/js_tests/query_timeline/fragment_metrics_diagram.test.js
similarity index 96%
rename from www/scripts/tests/query_timeline/fragment_metrics_diagram.test.js
rename to tests/webui/js_tests/query_timeline/fragment_metrics_diagram.test.js
index 080e2fd25..abd312db4 100644
--- a/www/scripts/tests/query_timeline/fragment_metrics_diagram.test.js
+++ b/tests/webui/js_tests/query_timeline/fragment_metrics_diagram.test.js
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-import {exportedForTest} from
"../../query_timeline/fragment_metrics_diagram.js";
+import {exportedForTest} from
"scripts/query_timeline/fragment_metrics_diagram.js";
describe("Test initializeFragmentMetrics", () => {
// Test whether aggregate arrays and time sample arrays are correctly
allocated
diff --git a/www/scripts/tests/query_timeline/host_utilization_diagram.test.js
b/tests/webui/js_tests/query_timeline/host_utilization_diagram.test.js
similarity index 97%
rename from www/scripts/tests/query_timeline/host_utilization_diagram.test.js
rename to tests/webui/js_tests/query_timeline/host_utilization_diagram.test.js
index 547d69cb8..2f5249351 100644
--- a/www/scripts/tests/query_timeline/host_utilization_diagram.test.js
+++ b/tests/webui/js_tests/query_timeline/host_utilization_diagram.test.js
@@ -16,7 +16,7 @@
// under the License.
import {describe, test, expect} from '@jest/globals';
-import {exportedForTest} from
"../../query_timeline/host_utilization_diagram.js";
+import {exportedForTest} from
"scripts/query_timeline/host_utilization_diagram.js";
describe("Test initializeUtilizationMetrics", () => {
// Test whether aggregate arrays and time sample arrays are correctly
allocated
diff --git a/tests/webui/linting/.gitignore b/tests/webui/linting/.gitignore
new file mode 100644
index 000000000..ccb2c800f
--- /dev/null
+++ b/tests/webui/linting/.gitignore
@@ -0,0 +1,2 @@
+node_modules/
+package-lock.json
\ No newline at end of file
diff --git a/tests/webui/linting/eslint.config.js
b/tests/webui/linting/eslint.config.js
new file mode 100644
index 000000000..4010184fd
--- /dev/null
+++ b/tests/webui/linting/eslint.config.js
@@ -0,0 +1,84 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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 globals = require("globals");
+
+module.exports = {
+ parserOptions : {
+ ecmaVersion : 2021,
+ sourceType : "module"
+ },
+ globals : globals.browser,
+ rules : {
+ // Warn for unused variables
+ "no-unused-vars" : "warn",
+ // Enforce strict equality (=== and !==)
+ "eqeqeq" : "error",
+ // Require curly braces for all control statements (if, while, etc.)
+ "curly" : ["error", "multi-line"],
+ // Enforce semicolons at the end of statements
+ "semi" : ["error", "always"],
+ // Enforce double quotes for strings
+ "quotes" : ["error", "double", {"allowTemplateLiterals" : true}],
+ // Set maximum line length to 90
+ "max-len" : ["error", {"code" : 90}],
+ // Disallow `var`, use `let` or `const`
+ "no-var" : "error",
+ // Prefer `const` where possible
+ "prefer-const" : "error",
+ // Disallow multiple empty lines
+ "no-multiple-empty-lines" : ["error",{max : 1}],
+ // Enforce spacing around infix operators (eg. +, =)
+ "space-infix-ops" : "error",
+ // Require parentheses around arrow function arguments
+ "arrow-parens" : ["error", "as-needed"],
+ // Require a space before if, function and other blocks
+ "space-before-blocks" : "error",
+ // Enforce consistent spacing inside braces
+ "object-curly-spacing" : ["error", "never"],
+ // Disallow shadowing variables declared in the outer scope
+ "no-shadow" : "error",
+ // Disallow constant conditions in if statements, loops, etc
+ "no-constant-condition" : "error",
+ // Disallow unnecessary parentheses in expressions
+ "no-extra-parens" : "error",
+ // Disallow duplicate arguments in function definitions
+ "no-dupe-args" : "error",
+ // Disallow duplicate keys in object literals
+ "no-dupe-keys" : "error",
+ // Disallow unreachable code after return, throw, continue, etc
+ "no-unreachable" : "error",
+ // Disallow reassigning function parameters
+ "no-param-reassign" : "error",
+ // Require functions to always return a value or to not return anything at
all
+ "consistent-return" : "error",
+ // Enforce consistent use of dot notation wherever possible
+ "dot-notation" : "error",
+ // Enforces spacing around the colon in object literal properties
+ "key-spacing" : ["error", {"beforeColon" : true}],
+ // Disallow optional chaining, where undefined values are not allowed
+ "no-unsafe-optional-chaining" : "error"
+ },
+ overrides: [
+ {
+ files : ["**/*.cjs"],
+ parserOptions: {
+ sourceType: "script"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/webui/linting/eslint/eslint_custom_output_formatter.js
b/tests/webui/linting/eslint/eslint_custom_output_formatter.js
new file mode 100644
index 000000000..cb73734a2
--- /dev/null
+++ b/tests/webui/linting/eslint/eslint_custom_output_formatter.js
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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");
+
+module.exports = function (results) {
+ const output = results.reduce((acc, result) => {
+ acc[path.relative(process.env.IMPALA_HOME, result.filePath)] =
+ result.messages.map(message => ({
+ message: message.message, // Include the error/warning message
+ line: message.line // Include the line number
+ }));
+ return acc;
+ }, {});
+ return JSON.stringify(output); // Pretty print the JSON output
+};
\ No newline at end of file
diff --git a/tests/webui/linting/package.json b/tests/webui/linting/package.json
new file mode 100644
index 000000000..3c671135d
--- /dev/null
+++ b/tests/webui/linting/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "impala-webui-scripts",
+ "dependencies": {
+ "eslint": "8.57.1",
+ "globals": "13.24.0"
+ }
+}
\ No newline at end of file
diff --git a/tests/webui/run-js-tests.sh b/tests/webui/run-js-tests.sh
new file mode 100755
index 000000000..58bad752f
--- /dev/null
+++ b/tests/webui/run-js-tests.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+set -euo pipefail
+. "$IMPALA_HOME/bin/report_build_error.sh"
+setup_report_build_error
+
+: ${IMPALA_JS_TEST_LOGS_DIR:="${IMPALA_LOGS_DIR}/js_tests"}
+
+. "$IMPALA_HOME/bin/nodejs/setup_nodejs.sh"
+
+NODEJS_BIN_PATH="${IMPALA_TOOLCHAIN}/node-${IMPALA_NODEJS_VERSION}/bin"
+NPM="${NODEJS_BIN_PATH}/npm"
+JS_TESTS_DIR="${IMPALA_HOME}/tests/webui/js_tests"
+export IMPALA_NODEJS="${NODEJS_BIN_PATH}/node"
+
+# Install packages in package.json
+"$IMPALA_NODEJS" "$NPM" --prefix "${JS_TESTS_DIR}" install
+
+# Run all JEST testing suites (by default *.test.js)
+NODE_PATH="${IMPALA_HOME}/www/" "$IMPALA_NODEJS" "$NPM" --prefix
"${JS_TESTS_DIR}" test
\ No newline at end of file
diff --git a/www/scripts/query_timeline/package.json
b/www/scripts/query_timeline/package.json
index 92b9ca8ae..17f261544 100644
--- a/www/scripts/query_timeline/package.json
+++ b/www/scripts/query_timeline/package.json
@@ -1,5 +1,4 @@
{
- "name": "Impala WebUI - Query Timeline",
- "version": "1.0.0",
+ "name": "query_timeline",
"type": "module"
-}
+}
\ No newline at end of file