Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ frontend-dev/
frontend-prod/
node_modules/
playwright-report/
src/polyfills/css-vars.js
69 changes: 69 additions & 0 deletions .github/workflows/immerse-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: immerse-pr

# ESLint also enforces the repository's Prettier rules.

on:
push:
branches:
- "**"
workflow_dispatch:
inputs:
ref:
description: "Branch or SHA to test"
type: string
default: ""

permissions:
contents: read

concurrency:
group: immerse-pr-${{ github.ref }}
cancel-in-progress: true

jobs:
immerse-pr:
name: immerse-pr
runs-on: linux-amd64-cpu16
timeout-minutes: 60
env:
# The PR checks do not use a browser. Avoid downloading Chromium during
# npm ci; the Puppeteer workflow installs it independently.
PUPPETEER_SKIP_DOWNLOAD: "true"
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: "true"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.sha }}
persist-credentials: false

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
cache-dependency-path: package-lock.json

- name: Upgrade npm
run: npm install --global npm@^11.6.1

- name: Use HTTPS for GitHub dependencies
run: |
git config --global --add \
url."https://github.com/".insteadOf \
"git@github.com:"
git config --global --add \
url."https://github.com/".insteadOf \
"ssh://git@github.com/"

- name: Install dependencies
run: npm ci

- name: Lint and check formatting
run: npm run lint

- name: Run unit tests
run: npm run test:unit

- name: Run component tests
run: npm run test:component
4 changes: 3 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const baseConfig = {
"^constants/(.*)$": "<rootDir>/src/constants/$1",
"^services/(.*)$": "<rootDir>/src/services/$1",
"^utils/(.*)$": "<rootDir>/src/utils/$1",
"^@heavyai/data-layer$":
"<rootDir>/node_modules/@heavyai/data-layer/packages/data-layer/dist/heavyai-data-layer.js",

"^vega-lite$": "<rootDir>/node_modules/vega-lite/build/vega-lite.min.js",
"^fs$": "<rootDir>/test-config/__mocks__/fs.js",
Expand All @@ -23,7 +25,7 @@ const baseConfig = {
"\\.[jt]sx?$": "babel-jest"
},
transformIgnorePatterns: [
"<rootDir>/node_modules/(?!(@heavyai|robust-predicates|delaunator|d3-.*|axios|internmap|formdata-polyfill|fetch-blob|data-uri-to-buffer|node-fetch|vega|suneditor|legendables|@mapbox-controls|@mapbox|vega-.*|cheerio|cheerio-.*))"
"<rootDir>/node_modules/(?!(@heavyai|robust-predicates|delaunator|d3-.*|axios|internmap|formdata-polyfill|fetch-blob|data-uri-to-buffer|node-fetch|vega|suneditor|quill-next|@quill-next|parchment|lodash-es|legendables|@mapbox-controls|@mapbox|vega-.*|cheerio|cheerio-.*))"
],
setupFiles: ["jest-canvas-mock", "<rootDir>/test-config/jest-shim"]
}
Expand Down
172 changes: 95 additions & 77 deletions scripts/generate-third-party-licenses.js
Original file line number Diff line number Diff line change
@@ -1,99 +1,117 @@
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

const fs = require('fs');
const path = require('path');
const licenseChecker = require('license-checker');
const fs = require("fs")
const path = require("path")
const licenseChecker = require("license-checker")

const OUTPUT_PATH = path.join(__dirname, '../third_party_licenses/THIRD_PARTY_LICENSES.md');
const PROJECT_ROOT = path.join(__dirname, '..');
const OUTPUT_PATH = path.join(
__dirname,
"../third_party_licenses/THIRD_PARTY_LICENSES.md"
)
const PROJECT_ROOT = path.join(__dirname, "..")

const spdxUrls = {
'0BSD': 'https://spdx.org/licenses/0BSD.html',
'Apache-2.0': 'https://spdx.org/licenses/Apache-2.0.html',
'Apache 2.0': 'https://spdx.org/licenses/Apache-2.0.html',
'Apache*': 'https://spdx.org/licenses/Apache-2.0.html',
'Apache-Style': 'https://spdx.org/licenses/Apache-2.0.html',
'(Apache-2.0 OR MPL-1.1)': 'https://spdx.org/licenses/Apache-2.0.html',
'Artistic-2.0': 'https://spdx.org/licenses/Artistic-2.0.html',
'BSD': 'https://spdx.org/licenses/BSD-2-Clause.html',
'BSD*': 'https://spdx.org/licenses/BSD-2-Clause.html',
'BSD-2-Clause': 'https://spdx.org/licenses/BSD-2-Clause.html',
'(BSD-2-Clause OR WTFPL)': 'https://spdx.org/licenses/BSD-2-Clause.html',
'BSD-3-Clause': 'https://spdx.org/licenses/BSD-3-Clause.html',
'(BSD-3-Clause OR GPL-2.0)': 'https://spdx.org/licenses/BSD-3-Clause.html',
'(AFL-2.1 OR BSD-3-Clause)': 'https://spdx.org/licenses/BSD-3-Clause.html',
'BlueOak-1.0.0': 'https://spdx.org/licenses/BlueOak-1.0.0.html',
'CC-BY-3.0': 'https://spdx.org/licenses/CC-BY-3.0.html',
'CC-BY-4.0': 'https://spdx.org/licenses/CC-BY-4.0.html',
'CC0-1.0': 'https://spdx.org/licenses/CC0-1.0.html',
'ISC': 'https://spdx.org/licenses/ISC.html',
'MIT': 'https://spdx.org/licenses/MIT.html',
'MIT*': 'https://spdx.org/licenses/MIT.html',
'MIT-0': 'https://spdx.org/licenses/MIT-0.html',
'MIT, CC-BY-SA-4.0': 'https://spdx.org/licenses/MIT.html',
'(MIT AND BSD-3-Clause)': 'https://spdx.org/licenses/MIT.html',
'(MIT AND CC-BY-3.0)': 'https://spdx.org/licenses/MIT.html',
'(MIT AND Zlib)': 'https://spdx.org/licenses/MIT.html',
'(MIT OR Apache-2.0)': 'https://spdx.org/licenses/MIT.html',
'(MIT OR CC0-1.0)': 'https://spdx.org/licenses/MIT.html',
'(MIT OR GPL-3.0-or-later)': 'https://spdx.org/licenses/MIT.html',
'(WTFPL OR MIT)': 'https://spdx.org/licenses/MIT.html',
'MPL-2.0': 'https://spdx.org/licenses/MPL-2.0.html',
'Python-2.0': 'https://spdx.org/licenses/Python-2.0.html',
'Unlicense': 'https://spdx.org/licenses/Unlicense.html',
'(Unlicense OR Apache-2.0)': 'https://spdx.org/licenses/Unlicense.html',
'UNLICENSED': '',
'WTFPL': 'https://spdx.org/licenses/WTFPL.html',
'Public Domain': '',
};
"0BSD": "https://spdx.org/licenses/0BSD.html",
"Apache-2.0": "https://spdx.org/licenses/Apache-2.0.html",
"Apache 2.0": "https://spdx.org/licenses/Apache-2.0.html",
"Apache*": "https://spdx.org/licenses/Apache-2.0.html",
"Apache-Style": "https://spdx.org/licenses/Apache-2.0.html",
"(Apache-2.0 OR MPL-1.1)": "https://spdx.org/licenses/Apache-2.0.html",
"Artistic-2.0": "https://spdx.org/licenses/Artistic-2.0.html",
BSD: "https://spdx.org/licenses/BSD-2-Clause.html",
"BSD*": "https://spdx.org/licenses/BSD-2-Clause.html",
"BSD-2-Clause": "https://spdx.org/licenses/BSD-2-Clause.html",
"(BSD-2-Clause OR WTFPL)": "https://spdx.org/licenses/BSD-2-Clause.html",
"BSD-3-Clause": "https://spdx.org/licenses/BSD-3-Clause.html",
"(BSD-3-Clause OR GPL-2.0)": "https://spdx.org/licenses/BSD-3-Clause.html",
"(AFL-2.1 OR BSD-3-Clause)": "https://spdx.org/licenses/BSD-3-Clause.html",
"BlueOak-1.0.0": "https://spdx.org/licenses/BlueOak-1.0.0.html",
"CC-BY-3.0": "https://spdx.org/licenses/CC-BY-3.0.html",
"CC-BY-4.0": "https://spdx.org/licenses/CC-BY-4.0.html",
"CC0-1.0": "https://spdx.org/licenses/CC0-1.0.html",
ISC: "https://spdx.org/licenses/ISC.html",
MIT: "https://spdx.org/licenses/MIT.html",
"MIT*": "https://spdx.org/licenses/MIT.html",
"MIT-0": "https://spdx.org/licenses/MIT-0.html",
"MIT, CC-BY-SA-4.0": "https://spdx.org/licenses/MIT.html",
"(MIT AND BSD-3-Clause)": "https://spdx.org/licenses/MIT.html",
"(MIT AND CC-BY-3.0)": "https://spdx.org/licenses/MIT.html",
"(MIT AND Zlib)": "https://spdx.org/licenses/MIT.html",
"(MIT OR Apache-2.0)": "https://spdx.org/licenses/MIT.html",
"(MIT OR CC0-1.0)": "https://spdx.org/licenses/MIT.html",
"(MIT OR GPL-3.0-or-later)": "https://spdx.org/licenses/MIT.html",
"(WTFPL OR MIT)": "https://spdx.org/licenses/MIT.html",
"MPL-2.0": "https://spdx.org/licenses/MPL-2.0.html",
"Python-2.0": "https://spdx.org/licenses/Python-2.0.html",
Unlicense: "https://spdx.org/licenses/Unlicense.html",
"(Unlicense OR Apache-2.0)": "https://spdx.org/licenses/Unlicense.html",
UNLICENSED: "",
WTFPL: "https://spdx.org/licenses/WTFPL.html",
"Public Domain": ""
}

function getLicenseUrl(license, repository) {
if (typeof license !== 'string') return repository || '';
if (license.startsWith('Custom: ')) {
return license.slice('Custom: '.length).trim();
if (typeof license !== "string") {
return repository || ""
}
if (license.startsWith("Custom: ")) {
return license.slice("Custom: ".length).trim()
}
const mapped = spdxUrls[license]
if (mapped !== undefined) {
return mapped
}
const mapped = spdxUrls[license];
if (mapped !== undefined) return mapped;
return repository || '';
return repository || ""
}

licenseChecker.init({ start: PROJECT_ROOT }, (err, data) => {
if (err) {
console.error('Error running license-checker:', err);
process.exit(1);
throw err
}

const rows = Object.entries(data).map(([pkgAtVersion, info]) => {
const lastAt = pkgAtVersion.lastIndexOf('@');
const name = pkgAtVersion.slice(0, lastAt);
const version = pkgAtVersion.slice(lastAt + 1);
const rawLicense = info.licenses;
const license = Array.isArray(rawLicense) ? rawLicense.join(', ') : (rawLicense || 'Unknown');
const licenseUrl = getLicenseUrl(license, info.repository);
return { name, version, license, licenseUrl };
});
const lastAt = pkgAtVersion.lastIndexOf("@")
const name = pkgAtVersion.slice(0, lastAt)
const version = pkgAtVersion.slice(lastAt + 1)
const rawLicense = info.licenses
const license = Array.isArray(rawLicense)
? rawLicense.join(", ")
: rawLicense || "Unknown"
const licenseUrl = getLicenseUrl(license, info.repository)
return { name, version, license, licenseUrl }
})

rows.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
rows.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))

const lines = [];
lines.push('# Third-Party Licenses');
lines.push('');
lines.push('This file lists the third-party npm packages distributed with this project (the runtime dependency closure of `package.json`) and their licenses, in fulfillment of the attribution requirements of those licenses.');
lines.push('');
lines.push('Generated from the installed environment. Total packages: **' + rows.length + '**.');
lines.push('');
lines.push('## Summary');
lines.push('');
lines.push('| Package | Version | License | URL |');
lines.push('|---|---|---|---|');
const lines = []
lines.push("# Third-Party Licenses")
lines.push("")
lines.push(
"This file lists the third-party npm packages distributed with this project (the runtime dependency closure of `package.json`) and their licenses, in fulfillment of the attribution requirements of those licenses."
)
lines.push("")
lines.push(
`Generated from the installed environment. Total packages: **${rows.length}**.`
)
lines.push("")
lines.push("## Summary")
lines.push("")
lines.push("| Package | Version | License | URL |")
lines.push("|---|---|---|---|")

for (const row of rows) {
const urlCell = row.licenseUrl ? `[${row.licenseUrl}](${row.licenseUrl})` : '';
lines.push(`| ${row.name} | ${row.version} | ${row.license} | ${urlCell} |`);
const urlCell = row.licenseUrl
? `[${row.licenseUrl}](${row.licenseUrl})`
: ""
lines.push(`| ${row.name} | ${row.version} | ${row.license} | ${urlCell} |`)
}

const output = lines.join('\n') + '\n';
fs.writeFileSync(OUTPUT_PATH, output);
console.log(`Written ${rows.length} packages to ${OUTPUT_PATH}`);
});
const output = `${lines.join("\n")}\n`
fs.writeFile(OUTPUT_PATH, output, (writeError) => {
if (writeError) {
throw writeError
}
process.stdout.write(`Written ${rows.length} packages to ${OUTPUT_PATH}\n`)
})
})
3 changes: 1 addition & 2 deletions src/actions/connection-action-creators.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ import {
setDatabaseStyles,
fetchConfigurationDB,
setThemeTint,
setCustomThemeEnabled,
SET_UI_THEME
setCustomThemeEnabled
} from "actions/user-configurable-ui-action-creators"
import { initializeAvailableBasemaps } from "charts/raster-chart/basemap"
import { isServiceError } from "../services/util/is-service-error.util"
Expand Down
32 changes: 32 additions & 0 deletions src/actions/dashboard-action-creators-basemap.unit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,36 @@ describe("dashboard-action-creators minimalist basemap", () => {
"minimalist"
)
})

it("updates imported dashboard titles inside tabs", async () => {
const services = new Map()
const createDashboardAsync = jest.fn().mockResolvedValue(undefined)
services.set("DbCon", {
getDashboardsAsync: jest.fn().mockResolvedValue([]),
createDashboardAsync
})

const importState = JSON.stringify({
tabs: {
tab1: {
dashboard: {
title: "Exported title"
}
}
}
})

await importDashboard("Imported title", "{}", importState)(
jest.fn(),
() => ({}),
services
)

const serializedImportedState = createDashboardAsync.mock.calls[0][1]
const decodedImportedState = decodeSerializedState(serializedImportedState)

expect(decodedImportedState.tabs.tab1.dashboard.title).toBe(
"Imported title"
)
})
})
2 changes: 1 addition & 1 deletion src/actions/dashboard-action-creators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ export const importDashboard = (title, metadata, state) => async (
const deserializedState = normalizeMinimalistBasemapState(JSON.parse(state))

const newTitleState = dashboardStateWithNewTitle(
state.tabs,
deserializedState.tabs,
deserializedState,
uniqueTitle
)
Expand Down
7 changes: 1 addition & 6 deletions src/charts/bubble/scatter.unit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ const spec = {
geoJson: null
}

let setEliminateNull = false

const crossfilter = {
dimension: () => ({
group: () => ({
Expand All @@ -106,10 +104,7 @@ const crossfilter = {
}),
binParams: noop
}),
setEliminateNull: (val) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
setEliminateNull = val
}
setEliminateNull: noop
})
}

Expand Down
7 changes: 1 addition & 6 deletions src/charts/heat/heat.unit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,6 @@ const spec = {
showColorPopup: false,
rangeFilter: []
}
let setEliminateNull = false

const crossfilter = {
dimension: () => ({
group: () => ({
Expand All @@ -141,10 +139,7 @@ const crossfilter = {
}),
binParams: noop
}),
setEliminateNull: (val) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
setEliminateNull = val
}
setEliminateNull: noop
})
}

Expand Down
Loading
Loading