diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 9c373616b26..00000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,606 +0,0 @@ -version: 2.1 -orbs: - browser-tools: circleci/browser-tools@2.4.0 - -# Inspired by: -# https://github.com/CircleCI-Public/circleci-demo-workflows/blob/workspace-forwarding/.circleci/config.yml -# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs -# -# For list of official CircleCI node.js images, go to: -# https://hub.docker.com/r/cimg/node/tags/ - -jobs: - install-and-cibuild: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - checkout - - run: - name: Set up build environment - command: .circleci/env_build.sh - - run: - name: Pretest - command: npm run pretest - - run: - name: CI-Build - command: npm run cibuild - - run: - name: Delete git - command: rm -rf .git - - persist_to_workspace: - root: /home/circleci - paths: - - plotly.js - - timezone-jasmine: - docker: - # need '-browsers' version to test in real (xvfb-wrapped) browsers - - image: cimg/node:18.20.4-browsers - working_directory: ~/plotly.js - steps: - - run: sudo apt-get update - - browser-tools/install_browser_tools: - install_firefox: false - install_geckodriver: false - install_chrome: true - chrome_version: "136.0.7103.113" - - attach_workspace: - at: ~/ - - run: - name: Run hover_label test in UTC timezone - environment: - TZ: "UTC" - command: date && npm run test-jasmine hover_label - - run: - name: Run hover_label test in Europe/Berlin timezone - environment: - TZ: "Europe/Berlin" - command: date && npm run test-jasmine hover_label - - run: - name: Run hover_label test in Asia/Tokyo timezone - environment: - TZ: "Asia/Tokyo" - command: date && npm run test-jasmine hover_label - - run: - name: Run hover_label test in America/Toronto timezone - environment: - TZ: "America/Toronto" - command: date && npm run test-jasmine hover_label - - no-gl-jasmine: - docker: - # need '-browsers' version to test in real (xvfb-wrapped) browsers - - image: cimg/node:18.20.4-browsers - environment: - # Alaska time (arbitrary timezone to test date logic) - TZ: "America/Anchorage" - parallelism: 4 - working_directory: ~/plotly.js - steps: - - run: sudo apt-get update - - browser-tools/install_browser_tools: - install_firefox: false - install_geckodriver: false - install_chrome: true - chrome_version: "136.0.7103.113" - - attach_workspace: - at: ~/ - - run: - name: Run jasmine tests (part A) - command: .circleci/test.sh no-gl-jasmine - - webgl-jasmine: - docker: - # need '-browsers' version to test in real (xvfb-wrapped) browsers - - image: cimg/node:18.20.4-browsers - environment: - # Alaska time (arbitrary timezone to test date logic) - TZ: "America/Anchorage" - parallelism: 8 - working_directory: ~/plotly.js - steps: - - run: sudo apt-get update - - browser-tools/install_browser_tools: - install_firefox: false - install_geckodriver: false - install_chrome: true - chrome_version: "136.0.7103.113" - - attach_workspace: - at: ~/ - - run: - name: Run jasmine tests (part B) - command: .circleci/test.sh webgl-jasmine - - virtual-webgl-jasmine: - docker: - # need '-browsers' version to test in real (xvfb-wrapped) browsers - - image: cimg/node:18.20.4-browsers - environment: - # Alaska time (arbitrary timezone to test date logic) - TZ: "America/Anchorage" - parallelism: 8 - working_directory: ~/plotly.js - steps: - - run: sudo apt-get update - - browser-tools/install_browser_tools: - install_firefox: false - install_geckodriver: false - install_chrome: true - chrome_version: "136.0.7103.113" - - attach_workspace: - at: ~/ - - run: - name: Run jasmine tests (part B) - command: .circleci/test.sh virtual-webgl-jasmine - - webgl-jasmine-chromeLatest: - docker: - # need '-browsers' version to test in real (xvfb-wrapped) browsers - - image: cimg/node:18.20.4-browsers - environment: - # Alaska time (arbitrary timezone to test date logic) - TZ: "America/Anchorage" - parallelism: 8 - working_directory: ~/plotly.js - steps: - - browser-tools/install_browser_tools: &browser-versions - install_firefox: false - install_geckodriver: false - install_chrome: true - chrome_version: "143.0.7499.192" # TEMPORARY pin until WebGL issues with 144 are resolved - - attach_workspace: - at: ~/ - - run: - name: Run jasmine tests (part B) - command: .circleci/test.sh webgl-jasmine - - flaky-no-gl-jasmine: - docker: - # need '-browsers' version to test in real (xvfb-wrapped) browsers - - image: cimg/node:18.20.4-browsers - environment: - # Alaska time (arbitrary timezone to test date logic) - TZ: "America/Anchorage" - working_directory: ~/plotly.js - steps: - - run: sudo apt-get update - - browser-tools/install_browser_tools: - install_firefox: false - install_geckodriver: false - install_chrome: true - chrome_version: "136.0.7103.113" - - attach_workspace: - at: ~/ - - run: - name: Run jasmine tests (part C) - command: .circleci/test.sh flaky-no-gl-jasmine - - bundle-jasmine: - docker: - # need '-browsers' version to test in real (xvfb-wrapped) browsers - - image: cimg/node:18.20.4-browsers - environment: - # Alaska time (arbitrary timezone to test date logic) - TZ: "America/Anchorage" - working_directory: ~/plotly.js - steps: - - run: sudo apt-get update - - browser-tools/install_browser_tools: - install_firefox: false - install_geckodriver: false - install_chrome: true - chrome_version: "136.0.7103.113" - - attach_workspace: - at: ~/ - - run: - name: Run jasmine tests (part D) - command: .circleci/test.sh bundle-jasmine - - mathjax-firefoxLatest: - docker: - # need '-browsers' version to test in real (xvfb-wrapped) browsers - - image: cimg/node:18.20.4-browsers - environment: - # Alaska time (arbitrary timezone to test date logic) - TZ: "America/Anchorage" - working_directory: ~/plotly.js - steps: - - browser-tools/install_browser_tools: - install_chrome: false - install_chromedriver: false - - attach_workspace: - at: ~/ - - run: - name: Test MathJax on firefox-latest - command: .circleci/test.sh mathjax-firefox - - make-baselines-virtual-webgl: - parallelism: 8 - docker: - - image: cimg/python:3.12.11 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: sudo apt-get update - - run: - name: Install kaleido, plotly.io and required fonts - command: .circleci/env_image.sh - - run: - name: Create png files using virtual-webgl & WebGL v1 - command: .circleci/test.sh make-baselines-virtual-webgl - - persist_to_workspace: - root: ~/ - paths: - - plotly.js - - make-baselines-mathjax3: - docker: - - image: cimg/python:3.12.11 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: sudo apt-get update - - run: - name: Install kaleido, plotly.io and required fonts - command: .circleci/env_image.sh - - run: - name: Create mathjax v3 png files - command: .circleci/test.sh make-baselines-mathjax3 - - persist_to_workspace: - root: ~/ - paths: - - plotly.js - - make-baselines: - parallelism: 12 - docker: - - image: cimg/python:3.12.11 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: sudo apt-get update - - run: - name: Install kaleido, plotly.io and required fonts - command: .circleci/env_image.sh - - run: - name: Create all png files - command: .circleci/test.sh make-baselines - - persist_to_workspace: - root: ~/ - paths: - - plotly.js - - make-baselines-b64: - parallelism: 12 - docker: - - image: cimg/python:3.12.11 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: sudo apt-get update - - run: - name: Install kaleido, plotly.io and required fonts - command: .circleci/env_image.sh - - run: - name: Create all png files - command: .circleci/test.sh make-baselines-b64 - - persist_to_workspace: - root: ~/ - paths: - - plotly.js - - test-baselines: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: - name: Compare pixels - command: .circleci/test.sh test-image ; find build -maxdepth 1 -type f -delete - - image-diff-message - - store_artifacts: - path: build - destination: / - - test-baselines-virtual-webgl: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: - name: Compare pixels - command: .circleci/test.sh test-image-virtual-webgl ; find build -maxdepth 1 -type f -delete - - image-diff-message - - store_artifacts: - path: build - destination: / - - test-baselines-b64: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: - name: Compare pixels - command: .circleci/test.sh test-image ; find build -maxdepth 1 -type f -delete - - image-diff-message - - store_artifacts: - path: build - destination: / - - test-baselines-mathjax3: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: - name: Compare pixels of mathjax v3 baselines - command: .circleci/test.sh test-image-mathjax3 - - image-diff-message - - store_artifacts: - path: build - destination: / - - make-exports: - docker: - - image: cimg/python:3.12.11 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: sudo apt-get update - - run: - name: Install kaleido, plotly.io and required fonts - command: .circleci/env_image.sh - - run: - name: Install poppler-utils to have pdftops for exporting eps - command: | - sudo apt-get update --allow-releaseinfo-change - sudo apt-get install poppler-utils - - run: - name: Create svg, jpg, jpeg, webp, pdf and eps files - command: sudo python3 test/image/make_exports.py - - persist_to_workspace: - root: ~/ - paths: - - plotly.js - - test-exports: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: - name: Test export sizes - command: node test/image/export_test.js ; find build -maxdepth 1 -type f -delete - - store_artifacts: - path: build - destination: / - - mock-validation: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: - name: Test validation using node.js and jsdom - command: npm run test-plain-obj - - run: - name: Validate mocks - command: npm run test-mock - - source-syntax: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - attach_workspace: - at: ~/ - - run: - name: Run syntax tests on source files - command: .circleci/test.sh source-syntax - - publish-dist: &publish-dist - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - checkout - - run: - name: Set up build environment - command: .circleci/env_build.sh - - run: - name: Preview CHANGELOG for next release (only on master) - command: | - if [ $CIRCLE_BRANCH == "master" ] - then npm run use-draftlogs && git --no-pager diff --color-words CHANGELOG.md || true - fi - - run: - name: Set draft version in package.json - command: | - node --eval "var fs = require('fs'); var inOut = './package.json'; var data = JSON.parse(fs.readFileSync(inOut)); var a = process.argv; data.version = a[a.length - 1].replace('v', ''); fs.writeFileSync(inOut, JSON.stringify(data, null, 2) + '\n');" `git describe` - - run: - name: View package.json diff between previous and next releases (including above draft version change) - command: git --no-pager diff --color-words tags/$(git describe --tags --abbrev=0) package.json || true - - run: - name: Build dist/ - command: npm run build - - store_artifacts: - path: dist - destination: dist - - run: - name: View dist/README.md diff between previous and next releases (including new bundle sizes) - command: git --no-pager diff --color-words tags/$(git describe --tags --abbrev=0) dist/README.md || true - - run: - name: Preview plot-schema diff between previous and next releases (only on master) - command: | - if [ $CIRCLE_BRANCH == "master" ] - then git --no-pager diff tags/$(git describe --tags --abbrev=0) dist/plot-schema.json || true - fi - - run: - name: Pack tarball - command: | - npm pack - version=$(node --eval "console.log(require('./package.json').version)") - mv plotly.js-$version.tgz plotly.js.tgz - - store_artifacts: - path: plotly.js.tgz - destination: /plotly.js.tgz - - run: - name: Show URLs to build files - command: | - PROJECT_NUM=45646037 - echo https://$CIRCLE_BUILD_NUM-$PROJECT_NUM-gh.circle-artifacts.com/0/plotly.js.tgz - echo https://$CIRCLE_BUILD_NUM-$PROJECT_NUM-gh.circle-artifacts.com/0/dist/plotly.js - echo https://$CIRCLE_BUILD_NUM-$PROJECT_NUM-gh.circle-artifacts.com/0/dist/plotly.min.js - echo https://$CIRCLE_BUILD_NUM-$PROJECT_NUM-gh.circle-artifacts.com/0/dist/plot-schema.json - - run: - name: Test plot-schema.json diff - If failed, after (npm start) you could run (npm run schema && git add test/plot-schema.json && git commit -m "update plot-schema diff") - command: diff --unified --color dist/plot-schema.json test/plot-schema.json - - publish-dist-node-v22: - <<: *publish-dist - docker: - - image: cimg/node:22.14.0 - - test-stackgl-bundle: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - checkout - - run: - name: Set up build environment - command: cd stackgl_modules && npm ci - - run: - name: Bundle bundle-stackgl/index.js - command: cd stackgl_modules && cp index.js INDEX.js && npm run bundle-stackgl - - run: - name: Test stackgl_modules/index.js diff - If failed please remember this file in auto generated and you should not modify it directly until a dependency change. To suggest changes please submit pull request to the relevant dependency. - command: diff --unified --color stackgl_modules/INDEX.js stackgl_modules/index.js - - store_artifacts: - path: stackgl_modules/index.js - destination: stackgl_modules/index.js - - test-topojson-build: - docker: - - image: cimg/node:18.20.4 - working_directory: ~/plotly.js - steps: - - checkout - - run: - name: Set up build environment - command: cd topojson && npm ci - - run: - name: Build topojson - command: cd topojson && mv dist dist_backup && npm run build - - run: - name: Compare existing files with newly built files. Any difference is a failure. A failure might mean that the source data changed. - command: diff -qr topojson/dist topojson/dist_backup - - run: - name: Compress artifacts - command: tar -cvzf topojson.tar topojson/dist - - store_artifacts: - path: topojson.tar - -commands: - image-diff-message: - steps: - - run: - name: IMAGE DIFF DETECTED - SEE NOTE BELOW - when: on_fail - command: | - echo "Image Diff Detected: baseline images may need to be updated. Run 'tasks/circleci_image_artifact_download.sh' to download the baseline images generated by this job." - echo "Add the new images to 'test/image/baselines/' and commit them to this pull request." - -workflows: - version: 2 - build-and-test: - max_auto_reruns: 1 - jobs: - - install-and-cibuild - - timezone-jasmine: - requires: - - install-and-cibuild - - bundle-jasmine: - requires: - - install-and-cibuild - - mathjax-firefoxLatest: - requires: - - install-and-cibuild - - no-gl-jasmine: - requires: - - install-and-cibuild - - webgl-jasmine: - requires: - - install-and-cibuild - - virtual-webgl-jasmine: - requires: - - install-and-cibuild - - webgl-jasmine-chromeLatest: - requires: - - install-and-cibuild - - flaky-no-gl-jasmine: - requires: - - install-and-cibuild - - make-baselines-virtual-webgl: - requires: - - install-and-cibuild - - test-baselines-virtual-webgl: - requires: - - make-baselines-virtual-webgl - - make-baselines-mathjax3: - requires: - - install-and-cibuild - - test-baselines-mathjax3: - requires: - - make-baselines-mathjax3 - - make-baselines-b64: - requires: - - install-and-cibuild - - test-baselines-b64: - requires: - - make-baselines-b64 - - make-baselines: - requires: - - install-and-cibuild - - test-baselines: - requires: - - make-baselines - - make-exports: - requires: - - install-and-cibuild - - test-exports: - requires: - - make-exports - - mock-validation: - requires: - - install-and-cibuild - - source-syntax: - requires: - - install-and-cibuild - - - publish-dist - - - publish-dist-node-v22 - - - test-stackgl-bundle - - - test-topojson-build diff --git a/.circleci/download_google_fonts.py b/.circleci/download_google_fonts.py deleted file mode 100644 index 8dc9dd7daa6..00000000000 --- a/.circleci/download_google_fonts.py +++ /dev/null @@ -1,82 +0,0 @@ -import os - -import requests - -dir_out = ".circleci/fonts/truetype/googleFonts/" - - -def download(repo, family, types, overwrite=True): - for t in types: - name = family + t + ".ttf" - url = repo + name + "?raw=true" - out_file = dir_out + name - print("Getting: ", url) - if os.path.exists(out_file) and not overwrite: - print(" => Already exists: ", out_file) - continue - req = requests.get(url, allow_redirects=False) - if req.status_code != 200: - # If we get a redirect, print an error so that we know to update the URL - if req.status_code == 302 or req.status_code == 301: - new_url = req.headers.get("Location") - print(f" => Redirected -- please update URL to: {new_url}") - raise RuntimeError(f""" -Download failed. -Status code: {req.status_code} -Message: {req.reason} -""") - open(out_file, "wb").write(req.content) - - -download( - "https://cdn.jsdelivr.net/gh/notofonts/notofonts.github.io/fonts/NotoSansMono/hinted/ttf/", - "NotoSansMono", - ["-Regular", "-Bold"], -) - -download( - "https://cdn.jsdelivr.net/gh/notofonts/notofonts.github.io/fonts/NotoSans/hinted/ttf/", - "NotoSans", - ["-Regular", "-Italic", "-Bold"], -) - -download( - "https://cdn.jsdelivr.net/gh/notofonts/notofonts.github.io/fonts/NotoSerif/hinted/ttf/", - "NotoSerif", - [ - "-Regular", - "-Italic", - "-Bold", - "-BoldItalic", - ], -) - -download( - "https://raw.githubusercontent.com/google/fonts/refs/heads/main/ofl/oldstandardtt/", - "OldStandard", - ["-Regular", "-Italic", "-Bold"], -) - -download( - "https://raw.githubusercontent.com/google/fonts/refs/heads/main/ofl/ptsansnarrow/", - "PT_Sans-Narrow-Web", - ["-Regular", "-Bold"], -) - -download( - "https://raw.githubusercontent.com/impallari/Raleway/refs/heads/master/fonts/v3.000%20Fontlab/TTF/", - "Raleway", - ["-Regular", "-Regular-Italic", "-Bold", "-Bold-Italic"], -) - -download( - "https://raw.githubusercontent.com/googlefonts/roboto-2/refs/heads/main/src/hinted/", - "Roboto", - ["-Regular", "-Italic", "-Bold", "-BoldItalic"], -) - -download( - "https://raw.githubusercontent.com/expo/google-fonts/refs/heads/main/font-packages/gravitas-one/400Regular/", - "GravitasOne", - ["_400Regular"], -) diff --git a/.circleci/env_image.sh b/.circleci/env_image.sh deleted file mode 100755 index 17e2f5bfa26..00000000000 --- a/.circleci/env_image.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -e -# install required fonts -sudo apt-get install fonts-liberation2 fonts-open-sans fonts-noto-cjk fonts-noto-color-emoji - -# install pip -sudo curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py -sudo python3 get-pip.py - -# install additional fonts -sudo python3 -m pip install requests -sudo python3 .circleci/download_google_fonts.py -sudo cp -r .circleci/fonts/ /usr/share/ -sudo apt install fontconfig -sudo fc-cache -f - -# install kaleido & plotly -sudo python3 -m pip install kaleido==0.2.1 plotly==6.2.0 --progress-bar off - -# install numpy i.e. to convert arrays to typed arrays -sudo python3 -m pip install numpy==1.24.2 diff --git a/.github/actions/run-xvfb/action.yml b/.github/actions/run-xvfb/action.yml new file mode 100644 index 00000000000..36b2d96fcfc --- /dev/null +++ b/.github/actions/run-xvfb/action.yml @@ -0,0 +1,13 @@ +name: 'Run with Xvfb' +description: 'Run a command under Xvfb with a preconfigured screen size' + +inputs: + run: + description: 'Command to execute' + required: true + +runs: + using: 'composite' + steps: + - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x1024x24" ${{ inputs.run }} + shell: bash diff --git a/.github/actions/setup-chrome/action.yml b/.github/actions/setup-chrome/action.yml new file mode 100644 index 00000000000..fe6bc48700b --- /dev/null +++ b/.github/actions/setup-chrome/action.yml @@ -0,0 +1,25 @@ +name: 'Setup Chrome' +description: 'Install Chrome and set CHROME_BIN environment variable' + +inputs: + chrome-version: + description: 'Chrome version to install' + default: '136.0.7103.113' + +runs: + using: 'composite' + steps: + - uses: browser-actions/setup-chrome@v2 + id: setup-chrome + with: + chrome-version: ${{ inputs.chrome-version }} + + - name: Set Chrome binary path + run: echo "CHROME_BIN=${{ steps.setup-chrome.outputs.chrome-path }}" >> $GITHUB_ENV + shell: bash + + - name: Verify Chrome version + run: | + echo "Chrome path: $CHROME_BIN" + $CHROME_BIN --version + shell: bash diff --git a/.github/actions/setup-image-env/action.yml b/.github/actions/setup-image-env/action.yml new file mode 100644 index 00000000000..86a84869a0a --- /dev/null +++ b/.github/actions/setup-image-env/action.yml @@ -0,0 +1,35 @@ +name: 'Setup Image Environment' +description: 'Setup Python, uv, and install Kaleido/plotly with required fonts' + +inputs: + python-version: + description: 'Python version to use' + default: '3.12' + uv-version: + description: 'uv version to use' + default: '0.11.2' + +runs: + using: 'composite' + steps: + - uses: actions/setup-python@v6 + with: + python-version: ${{ inputs.python-version }} + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + version: ${{ inputs.uv-version }} + + - name: Cache apt font packages + uses: actions/cache@v4 + id: apt-cache + with: + path: ~/.cache/apt-fonts + key: apt-fonts-${{ runner.os }}-${{ hashFiles('.github/scripts/env_image.sh') }} + + - name: Install Kaleido, plotly.io and required fonts + run: .github/scripts/env_image.sh + shell: bash + env: + APT_CACHE_HIT: ${{ steps.apt-cache.outputs.cache-hit }} diff --git a/.github/actions/setup-workspace/action.yml b/.github/actions/setup-workspace/action.yml new file mode 100644 index 00000000000..b21fc046a46 --- /dev/null +++ b/.github/actions/setup-workspace/action.yml @@ -0,0 +1,26 @@ +name: 'Setup Workspace' +description: 'Setup Node.js, install dependencies, and download build artifacts' + +inputs: + node-version: + description: 'Node.js version to use' + default: '18.20.4' + +runs: + using: 'composite' + steps: + - uses: actions/setup-node@v6 + with: + node-version: ${{ inputs.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + shell: bash + env: + NODE_OPTIONS: '--max-old-space-size=4096' + + - uses: actions/download-artifact@v8 + with: + name: build-output + path: . diff --git a/.circleci/fonts/truetype/googleFonts/GravitasOne_400Regular.ttf b/.github/fonts/truetype/googleFonts/GravitasOne_400Regular.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/GravitasOne_400Regular.ttf rename to .github/fonts/truetype/googleFonts/GravitasOne_400Regular.ttf diff --git a/.circleci/fonts/truetype/googleFonts/NotoSans-Bold.ttf b/.github/fonts/truetype/googleFonts/NotoSans-Bold.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/NotoSans-Bold.ttf rename to .github/fonts/truetype/googleFonts/NotoSans-Bold.ttf diff --git a/.circleci/fonts/truetype/googleFonts/NotoSans-Italic.ttf b/.github/fonts/truetype/googleFonts/NotoSans-Italic.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/NotoSans-Italic.ttf rename to .github/fonts/truetype/googleFonts/NotoSans-Italic.ttf diff --git a/.circleci/fonts/truetype/googleFonts/NotoSans-Regular.ttf b/.github/fonts/truetype/googleFonts/NotoSans-Regular.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/NotoSans-Regular.ttf rename to .github/fonts/truetype/googleFonts/NotoSans-Regular.ttf diff --git a/.circleci/fonts/truetype/googleFonts/NotoSansMono-Bold.ttf b/.github/fonts/truetype/googleFonts/NotoSansMono-Bold.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/NotoSansMono-Bold.ttf rename to .github/fonts/truetype/googleFonts/NotoSansMono-Bold.ttf diff --git a/.circleci/fonts/truetype/googleFonts/NotoSansMono-Regular.ttf b/.github/fonts/truetype/googleFonts/NotoSansMono-Regular.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/NotoSansMono-Regular.ttf rename to .github/fonts/truetype/googleFonts/NotoSansMono-Regular.ttf diff --git a/.circleci/fonts/truetype/googleFonts/NotoSerif-Bold.ttf b/.github/fonts/truetype/googleFonts/NotoSerif-Bold.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/NotoSerif-Bold.ttf rename to .github/fonts/truetype/googleFonts/NotoSerif-Bold.ttf diff --git a/.circleci/fonts/truetype/googleFonts/NotoSerif-BoldItalic.ttf b/.github/fonts/truetype/googleFonts/NotoSerif-BoldItalic.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/NotoSerif-BoldItalic.ttf rename to .github/fonts/truetype/googleFonts/NotoSerif-BoldItalic.ttf diff --git a/.circleci/fonts/truetype/googleFonts/NotoSerif-Italic.ttf b/.github/fonts/truetype/googleFonts/NotoSerif-Italic.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/NotoSerif-Italic.ttf rename to .github/fonts/truetype/googleFonts/NotoSerif-Italic.ttf diff --git a/.circleci/fonts/truetype/googleFonts/NotoSerif-Regular.ttf b/.github/fonts/truetype/googleFonts/NotoSerif-Regular.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/NotoSerif-Regular.ttf rename to .github/fonts/truetype/googleFonts/NotoSerif-Regular.ttf diff --git a/.circleci/fonts/truetype/googleFonts/OldStandard-Bold.ttf b/.github/fonts/truetype/googleFonts/OldStandard-Bold.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/OldStandard-Bold.ttf rename to .github/fonts/truetype/googleFonts/OldStandard-Bold.ttf diff --git a/.circleci/fonts/truetype/googleFonts/OldStandard-Italic.ttf b/.github/fonts/truetype/googleFonts/OldStandard-Italic.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/OldStandard-Italic.ttf rename to .github/fonts/truetype/googleFonts/OldStandard-Italic.ttf diff --git a/.circleci/fonts/truetype/googleFonts/OldStandard-Regular.ttf b/.github/fonts/truetype/googleFonts/OldStandard-Regular.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/OldStandard-Regular.ttf rename to .github/fonts/truetype/googleFonts/OldStandard-Regular.ttf diff --git a/.circleci/fonts/truetype/googleFonts/PT_Sans-Narrow-Web-Bold.ttf b/.github/fonts/truetype/googleFonts/PT_Sans-Narrow-Web-Bold.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/PT_Sans-Narrow-Web-Bold.ttf rename to .github/fonts/truetype/googleFonts/PT_Sans-Narrow-Web-Bold.ttf diff --git a/.circleci/fonts/truetype/googleFonts/PT_Sans-Narrow-Web-Regular.ttf b/.github/fonts/truetype/googleFonts/PT_Sans-Narrow-Web-Regular.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/PT_Sans-Narrow-Web-Regular.ttf rename to .github/fonts/truetype/googleFonts/PT_Sans-Narrow-Web-Regular.ttf diff --git a/.circleci/fonts/truetype/googleFonts/Raleway-Bold-Italic.ttf b/.github/fonts/truetype/googleFonts/Raleway-Bold-Italic.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/Raleway-Bold-Italic.ttf rename to .github/fonts/truetype/googleFonts/Raleway-Bold-Italic.ttf diff --git a/.circleci/fonts/truetype/googleFonts/Raleway-Bold.ttf b/.github/fonts/truetype/googleFonts/Raleway-Bold.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/Raleway-Bold.ttf rename to .github/fonts/truetype/googleFonts/Raleway-Bold.ttf diff --git a/.circleci/fonts/truetype/googleFonts/Raleway-Regular-Italic.ttf b/.github/fonts/truetype/googleFonts/Raleway-Regular-Italic.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/Raleway-Regular-Italic.ttf rename to .github/fonts/truetype/googleFonts/Raleway-Regular-Italic.ttf diff --git a/.circleci/fonts/truetype/googleFonts/Raleway-Regular.ttf b/.github/fonts/truetype/googleFonts/Raleway-Regular.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/Raleway-Regular.ttf rename to .github/fonts/truetype/googleFonts/Raleway-Regular.ttf diff --git a/.circleci/fonts/truetype/googleFonts/Roboto-Bold.ttf b/.github/fonts/truetype/googleFonts/Roboto-Bold.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/Roboto-Bold.ttf rename to .github/fonts/truetype/googleFonts/Roboto-Bold.ttf diff --git a/.circleci/fonts/truetype/googleFonts/Roboto-BoldItalic.ttf b/.github/fonts/truetype/googleFonts/Roboto-BoldItalic.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/Roboto-BoldItalic.ttf rename to .github/fonts/truetype/googleFonts/Roboto-BoldItalic.ttf diff --git a/.circleci/fonts/truetype/googleFonts/Roboto-Italic.ttf b/.github/fonts/truetype/googleFonts/Roboto-Italic.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/Roboto-Italic.ttf rename to .github/fonts/truetype/googleFonts/Roboto-Italic.ttf diff --git a/.circleci/fonts/truetype/googleFonts/Roboto-Regular.ttf b/.github/fonts/truetype/googleFonts/Roboto-Regular.ttf similarity index 100% rename from .circleci/fonts/truetype/googleFonts/Roboto-Regular.ttf rename to .github/fonts/truetype/googleFonts/Roboto-Regular.ttf diff --git a/.circleci/env_build.sh b/.github/scripts/env_build.sh similarity index 100% rename from .circleci/env_build.sh rename to .github/scripts/env_build.sh diff --git a/.github/scripts/env_image.sh b/.github/scripts/env_image.sh new file mode 100755 index 00000000000..a27204a3c1e --- /dev/null +++ b/.github/scripts/env_image.sh @@ -0,0 +1,36 @@ +#!/bin/sh +set -e + +APT_PACKAGES="fonts-liberation2 fonts-open-sans fonts-noto-cjk fonts-noto-color-emoji fontconfig" +APT_CACHE_DIR="${HOME}/.cache/apt-fonts" + +if [ "$APT_CACHE_HIT" = "true" ] && [ -d "$APT_CACHE_DIR" ]; then + echo "Installing font packages from cache..." + sudo dpkg -i "$APT_CACHE_DIR"/*.deb 2>/dev/null || sudo apt-get install -yf +else + echo "Downloading and installing font packages..." + sudo apt-get update -q + sudo apt-get install -y --no-install-recommends $APT_PACKAGES + # Save debs for future cache + mkdir -p "$APT_CACHE_DIR" + for pkg in $APT_PACKAGES; do + cp /var/cache/apt/archives/${pkg}_*.deb "$APT_CACHE_DIR/" 2>/dev/null || true + done +fi + +# Rebuild font cache +sudo fc-cache -f + +# Install additional fonts (committed in .github/fonts/) +sudo cp -r .github/fonts/ /usr/share/ +sudo fc-cache -f + +# Install Kaleido & Plotly +uv pip install --system kaleido==1.2 plotly==6.6.0 --no-progress + +# Install numpy i.e. to convert arrays to typed arrays +uv pip install --system numpy==2.4.3 + +# Verify version of python and versions of installed python packages +python --version +uv pip freeze --system diff --git a/.github/scripts/split_files.mjs b/.github/scripts/split_files.mjs new file mode 100755 index 00000000000..8841ab33ea8 --- /dev/null +++ b/.github/scripts/split_files.mjs @@ -0,0 +1,20 @@ +#!/usr/bin/env node + +// Replacement for `circleci tests split` +// Reads lines from stdin and emits only those where index % SHARD_TOTAL == SHARD_INDEX +// Environment variables SHARD_INDEX and SHARD_TOTAL must be set. + +import { readFileSync } from 'fs'; + +const lines = readFileSync('/dev/stdin', 'utf8').trim().split('\n').filter(Boolean); +const index = parseInt(process.env.SHARD_INDEX); +const total = parseInt(process.env.SHARD_TOTAL); + +if (isNaN(index) || isNaN(total) || total <= 0) { + console.error('SHARD_INDEX and SHARD_TOTAL environment variables must be set to valid integers'); + process.exit(1); +} + +lines.forEach((line, i) => { + if (i % total === index) console.log(line); +}); diff --git a/.circleci/test.sh b/.github/scripts/test.sh similarity index 71% rename from .circleci/test.sh rename to .github/scripts/test.sh index 4fc876018b8..7bd42ff6feb 100755 --- a/.circleci/test.sh +++ b/.github/scripts/test.sh @@ -1,10 +1,13 @@ #!/bin/bash -# override CircleCi's default run settings +# GitHub Actions version of .circleci/test.sh +# Replaces `circleci tests split` with split_files.mjs + set +e set +o pipefail -ROOT=$(dirname $0)/.. +ROOT=$(dirname $0)/../.. +SPLIT="$ROOT/.github/scripts/split_files.mjs" EXIT_STATE=0 MAX_AUTO_RETRY=0 @@ -33,10 +36,13 @@ retry () { fi } +# Ensure output directories exist (not present in fresh GHA checkout) +mkdir -p build/test_images + case $1 in no-gl-jasmine) - SUITE=$(circleci tests glob "$ROOT/test/jasmine/tests/*" | circleci tests split) + SUITE=$(ls -1 $ROOT/test/jasmine/tests/* | sort | node "$SPLIT") MAX_AUTO_RETRY=2 retry npm run test-jasmine -- $SUITE --skip-tags=gl,noCI,flaky || EXIT_STATE=$? @@ -44,7 +50,7 @@ case $1 in ;; webgl-jasmine) - SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=5 --tag=gl | circleci tests split)) + SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=5 --tag=gl | node "$SPLIT")) for s in ${SHARDS[@]}; do MAX_AUTO_RETRY=2 retry npm run test-jasmine -- "$s" --tags=gl --skip-tags=noCI --doNotFailOnEmptyTestSuite @@ -54,7 +60,7 @@ case $1 in ;; virtual-webgl-jasmine) - SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=5 --tag=gl | circleci tests split)) + SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=5 --tag=gl | node "$SPLIT")) for s in ${SHARDS[@]}; do MAX_AUTO_RETRY=2 retry ./node_modules/karma/bin/karma start test/jasmine/karma.conf.js --virtualWebgl --tags=gl --skip-tags=noCI,noVirtualWebgl --doNotFailOnEmptyTestSuite -- "$s" @@ -64,7 +70,7 @@ case $1 in ;; flaky-no-gl-jasmine) - SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=1 --tag=flaky | circleci tests split)) + SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=1 --tag=flaky | node "$SPLIT")) for s in ${SHARDS[@]}; do MAX_AUTO_RETRY=5 @@ -96,40 +102,46 @@ case $1 in SUITE=$({\ find $ROOT/test/image/mocks/gl* -type f -printf "%f\n"; \ find $ROOT/test/image/mocks/map* -type f -printf "%f\n"; \ - } | sed 's/\.json$//1' | circleci tests split) - sudo python3 test/image/make_baseline.py virtual-webgl $SUITE || EXIT_STATE=$? + } | sed 's/\.json$//1' | sort | node "$SPLIT") + python test/image/make_baseline.py virtual-webgl $SUITE || EXIT_STATE=$? exit $EXIT_STATE ;; make-baselines-mathjax3) - sudo python3 test/image/make_baseline.py mathjax3 legend_mathjax_title_and_items mathjax parcats_grid_subplots table_latex_multitrace_scatter table_plain_birds table_wrapped_birds ternary-mathjax ternary-mathjax-title-place-subtitle || EXIT_STATE=$? + MATHJAX3_MOCKS=$(jq -r '.compare_mathjax3 | join(" ")' test/image/compare_pixels_collections.json) + python test/image/make_baseline.py mathjax3 $MATHJAX3_MOCKS || EXIT_STATE=$? exit $EXIT_STATE ;; make-baselines-b64) - SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | circleci tests split) - sudo python3 test/image/make_baseline.py b64 $SUITE || EXIT_STATE=$? + SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | sort | node "$SPLIT") + python test/image/make_baseline.py b64 $SUITE || EXIT_STATE=$? exit $EXIT_STATE ;; make-baselines) - SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | circleci tests split) - sudo python3 test/image/make_baseline.py $SUITE || EXIT_STATE=$? + SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | sort | node "$SPLIT") + python test/image/make_baseline.py $SUITE || EXIT_STATE=$? + exit $EXIT_STATE + ;; + + make-exports) + python test/image/make_exports.py || EXIT_STATE=$? exit $EXIT_STATE ;; test-image) - node test/image/compare_pixels_test.js || { tar -cvf build/baselines.tar build/test_images/*.png ; exit 1 ; } || EXIT_STATE=$? + node test/image/compare_pixels_test.mjs || EXIT_STATE=$? exit $EXIT_STATE ;; test-image-mathjax3) - node test/image/compare_pixels_test.js mathjax3 || { tar -cvf build/baselines.tar build/test_images/*.png ; exit 1 ; } || EXIT_STATE=$? + node test/image/compare_pixels_test.mjs mathjax3 || EXIT_STATE=$? exit $EXIT_STATE ;; test-image-virtual-webgl) - node test/image/compare_pixels_test.js virtual-webgl || { tar -cvf build/baselines.tar build/test_images/*.png ; exit 1 ; } || EXIT_STATE=$? + node test/image/compare_pixels_test.mjs virtual-webgl || EXIT_STATE=$? exit $EXIT_STATE ;; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..e9d52b8bd5f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,712 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +concurrency: + group: ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + # TODO: Update this version to match library + NODE_VERSION: '18.20.4' + +jobs: + # ============================================================ + # Root build job - all dependent jobs fan out from here + # ============================================================ + install-and-cibuild: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + env: + NODE_OPTIONS: '--max-old-space-size=4096' + + - name: Pretest + run: npm run pretest + + - name: CI-Build + run: npm run cibuild + + - uses: actions/upload-artifact@v7 + with: + name: build-output + retention-days: 3 + path: | + build/ + lib/ + src/version.js + + # ============================================================ + # Jasmine browser tests + # ============================================================ + timezone-jasmine: + needs: install-and-cibuild + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + - uses: ./.github/actions/setup-chrome + + - name: Run hover_label test in UTC timezone + uses: ./.github/actions/run-xvfb + env: + TZ: 'UTC' + with: + run: npm run test-jasmine hover_label + + - name: Run hover_label test in Europe/Berlin timezone + uses: ./.github/actions/run-xvfb + env: + TZ: 'Europe/Berlin' + with: + run: npm run test-jasmine hover_label + + - name: Run hover_label test in Asia/Tokyo timezone + uses: ./.github/actions/run-xvfb + env: + TZ: 'Asia/Tokyo' + with: + run: npm run test-jasmine hover_label + + - name: Run hover_label test in America/Toronto timezone + uses: ./.github/actions/run-xvfb + env: + TZ: 'America/Toronto' + with: + run: npm run test-jasmine hover_label + + no-gl-jasmine: + needs: install-and-cibuild + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3] + env: + TZ: 'America/Anchorage' + SHARD_INDEX: ${{ matrix.shard }} + SHARD_TOTAL: 4 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + - uses: ./.github/actions/setup-chrome + + - name: Run jasmine tests (no-gl, shard ${{ matrix.shard }}) + uses: ./.github/actions/run-xvfb + with: + run: .github/scripts/test.sh no-gl-jasmine + + webgl-jasmine: + needs: install-and-cibuild + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3, 4, 5, 6, 7] + env: + TZ: 'America/Anchorage' + SHARD_INDEX: ${{ matrix.shard }} + SHARD_TOTAL: 8 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + - uses: ./.github/actions/setup-chrome + + - name: Run jasmine tests (webgl, shard ${{ matrix.shard }}) + uses: ./.github/actions/run-xvfb + with: + run: .github/scripts/test.sh webgl-jasmine + + virtual-webgl-jasmine: + needs: install-and-cibuild + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3, 4, 5, 6, 7] + env: + TZ: 'America/Anchorage' + SHARD_INDEX: ${{ matrix.shard }} + SHARD_TOTAL: 8 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + - uses: ./.github/actions/setup-chrome + + - name: Run jasmine tests (virtual-webgl, shard ${{ matrix.shard }}) + uses: ./.github/actions/run-xvfb + with: + run: .github/scripts/test.sh virtual-webgl-jasmine + + webgl-jasmine-chromeLatest: + needs: install-and-cibuild + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3, 4, 5, 6, 7] + env: + TZ: 'America/Anchorage' + SHARD_INDEX: ${{ matrix.shard }} + SHARD_TOTAL: 8 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + - uses: ./.github/actions/setup-chrome + with: + chrome-version: '143.0.7499.192' + + - name: Run jasmine tests (webgl chromeLatest, shard ${{ matrix.shard }}) + uses: ./.github/actions/run-xvfb + with: + run: .github/scripts/test.sh webgl-jasmine + + flaky-no-gl-jasmine: + needs: install-and-cibuild + runs-on: ubuntu-latest + env: + TZ: 'America/Anchorage' + SHARD_INDEX: 0 + SHARD_TOTAL: 1 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + - uses: ./.github/actions/setup-chrome + + - name: Run flaky jasmine tests + uses: ./.github/actions/run-xvfb + with: + run: .github/scripts/test.sh flaky-no-gl-jasmine + + bundle-jasmine: + needs: install-and-cibuild + runs-on: ubuntu-latest + env: + TZ: 'America/Anchorage' + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + - uses: ./.github/actions/setup-chrome + + - name: Run bundle jasmine tests + uses: ./.github/actions/run-xvfb + with: + run: .github/scripts/test.sh bundle-jasmine + + mathjax-firefoxLatest: + needs: install-and-cibuild + runs-on: ubuntu-latest + env: + TZ: 'America/Anchorage' + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: browser-actions/setup-firefox@v1 + + - name: Test MathJax on firefox-latest + uses: ./.github/actions/run-xvfb + with: + run: .github/scripts/test.sh mathjax-firefox + + # ============================================================ + # noCI tests + # ============================================================ + noci-jasmine: + needs: install-and-cibuild + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + - uses: ./.github/actions/setup-chrome + with: + chrome-version: 'stable' + + - name: Run noCI tests + uses: coactions/setup-xvfb@v1 + with: + run: ./tasks/noci_test.sh jasmine + + # ============================================================ + # Image baseline generation and comparison + # ============================================================ + make-baselines: + needs: install-and-cibuild + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + env: + SHARD_INDEX: ${{ matrix.shard }} + SHARD_TOTAL: 12 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: ./.github/actions/setup-image-env + + - name: Create all png files + run: .github/scripts/test.sh make-baselines + + - uses: actions/upload-artifact@v7 + with: + name: baselines-default-${{ matrix.shard }} + retention-days: 3 + path: build/test_images/ + + test-baselines: + needs: make-baselines + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: actions/download-artifact@v8 + with: + pattern: baselines-default-* + path: build/test_images/ + merge-multiple: true + + - name: Compare pixels + run: .github/scripts/test.sh test-image + - name: List diff folder contents + if: failure() + run: | + echo "=== build/test_images/ ===" + ls -la build/test_images/ 2>/dev/null || echo "(empty or missing)" + echo "=== build/test_images_diff/ ===" + ls -la build/test_images_diff/ 2>/dev/null || echo "(empty or missing)" + - name: IMAGE DIFF DETECTED + if: failure() + run: | + echo "::warning::Image Diff Detected: baseline images may need to be updated." + echo "Download the baseline images from the artifacts of this workflow run." + echo "Add the new images to 'test/image/baselines/' and commit them to this pull request." + + - uses: actions/upload-artifact@v7 + if: failure() + with: + name: baselines-default-diff + retention-days: 7 + path: | + build/test_images/ + build/test_images_diff/ + + make-baselines-b64: + needs: install-and-cibuild + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + env: + SHARD_INDEX: ${{ matrix.shard }} + SHARD_TOTAL: 12 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: ./.github/actions/setup-image-env + + - name: Create all png files (b64) + run: .github/scripts/test.sh make-baselines-b64 + + - uses: actions/upload-artifact@v7 + with: + name: baselines-b64-${{ matrix.shard }} + retention-days: 3 + path: build/test_images/ + + test-baselines-b64: + needs: make-baselines-b64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: actions/download-artifact@v8 + with: + pattern: baselines-b64-* + path: build/test_images/ + merge-multiple: true + + - name: Compare pixels + run: .github/scripts/test.sh test-image + - name: IMAGE DIFF DETECTED + if: failure() + run: | + echo "::warning::Image Diff Detected: baseline images may need to be updated." + echo "Download the baseline images from the artifacts of this workflow run." + echo "Add the new images to 'test/image/baselines/' and commit them to this pull request." + + - uses: actions/upload-artifact@v7 + if: failure() + with: + name: baselines-b64-diff + retention-days: 7 + path: | + build/test_images/ + build/test_images_diff/ + + make-baselines-virtual-webgl: + needs: install-and-cibuild + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3, 4, 5, 6, 7] + env: + SHARD_INDEX: ${{ matrix.shard }} + SHARD_TOTAL: 8 + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: ./.github/actions/setup-image-env + + - name: Create png files (virtual-webgl) + run: .github/scripts/test.sh make-baselines-virtual-webgl + + - uses: actions/upload-artifact@v7 + with: + name: baselines-virtual-webgl-${{ matrix.shard }} + retention-days: 3 + path: build/test_images/ + + test-baselines-virtual-webgl: + needs: make-baselines-virtual-webgl + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: actions/download-artifact@v8 + with: + pattern: baselines-virtual-webgl-* + path: build/test_images/ + merge-multiple: true + + - name: Compare pixels + run: .github/scripts/test.sh test-image-virtual-webgl + - name: IMAGE DIFF DETECTED + if: failure() + run: | + echo "::warning::Image Diff Detected: baseline images may need to be updated." + echo "Download the baseline images from the artifacts of this workflow run." + echo "Add the new images to 'test/image/baselines/' and commit them to this pull request." + + - uses: actions/upload-artifact@v7 + if: failure() + with: + name: baselines-virtual-webgl-diff + retention-days: 7 + path: | + build/test_images/ + build/test_images_diff/ + + make-baselines-mathjax3: + needs: install-and-cibuild + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: ./.github/actions/setup-image-env + + - name: Create mathjax v3 png files + run: .github/scripts/test.sh make-baselines-mathjax3 + + - uses: actions/upload-artifact@v7 + with: + name: baselines-mathjax3 + retention-days: 3 + path: build/test_images/ + + test-baselines-mathjax3: + needs: make-baselines-mathjax3 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: actions/download-artifact@v8 + with: + name: baselines-mathjax3 + path: build/test_images/ + + - name: Compare pixels of mathjax v3 baselines + run: .github/scripts/test.sh test-image-mathjax3 + + - name: IMAGE DIFF DETECTED + if: failure() + run: | + echo "::warning::Image Diff Detected: baseline images may need to be updated." + echo "Download the baseline images from the artifacts of this workflow run." + echo "Add the new images to 'test/image/baselines/' and commit them to this pull request." + + - uses: actions/upload-artifact@v7 + if: failure() + with: + name: baselines-mathjax3-diff + retention-days: 7 + path: | + build/test_images/ + build/test_images_diff/ + + # ============================================================ + # Export generation and testing + # ============================================================ + make-exports: + needs: install-and-cibuild + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: ./.github/actions/setup-image-env + + - name: Install poppler-utils for eps export + run: | + sudo apt-get update --allow-releaseinfo-change + sudo apt-get install poppler-utils + + - name: Create svg, jpg, jpeg, webp, pdf and eps files + run: .github/scripts/test.sh make-exports + + - uses: actions/upload-artifact@v7 + with: + name: exports + retention-days: 3 + path: build/ + + test-exports: + needs: make-exports + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - uses: actions/download-artifact@v8 + with: + name: exports + path: build/ + + - name: Test export sizes + run: node test/image/export_test.js + - uses: actions/upload-artifact@v7 + if: failure() + with: + name: exports-diff + retention-days: 7 + path: build/ + + # ============================================================ + # Validation jobs + # ============================================================ + mock-validation: + needs: install-and-cibuild + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - name: Test validation using node.js and jsdom + run: npm run test-plain-obj + + - name: Validate mocks + run: npm run test-mock + + source-syntax: + needs: install-and-cibuild + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup-workspace + + - name: Run syntax tests on source files + run: .github/scripts/test.sh source-syntax + + # ============================================================ + # Standalone jobs (no dependencies on install-and-cibuild) + # ============================================================ + publish-dist: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Set up build environment + run: .github/scripts/env_build.sh + + - name: Preview CHANGELOG for next release (only on master) + if: github.ref == 'refs/heads/master' + run: npm run use-draftlogs && git --no-pager diff --color-words CHANGELOG.md || true + + - name: Set draft version in package.json + run: | + node --eval "var fs = require('fs'); var inOut = './package.json'; var data = JSON.parse(fs.readFileSync(inOut)); var a = process.argv; data.version = a[a.length - 1].replace('v', ''); fs.writeFileSync(inOut, JSON.stringify(data, null, 2) + '\n');" $(git describe) + + - name: View package.json diff between previous and next releases + run: git --no-pager diff --color-words tags/$(git describe --tags --abbrev=0) package.json || true + + - name: Build dist/ + run: npm run build + + - uses: actions/upload-artifact@v7 + with: + name: dist-node18 + retention-days: 7 + path: dist/ + + - name: View dist/README.md diff between previous and next releases + run: git --no-pager diff --color-words tags/$(git describe --tags --abbrev=0) dist/README.md || true + + - name: Preview plot-schema diff (only on master) + if: github.ref == 'refs/heads/master' + run: git --no-pager diff tags/$(git describe --tags --abbrev=0) dist/plot-schema.json || true + + - name: Pack tarball + run: | + npm pack + version=$(node --eval "console.log(require('./package.json').version)") + mv plotly.js-$version.tgz plotly.js.tgz + + - uses: actions/upload-artifact@v7 + with: + name: tarball-node18 + retention-days: 7 + path: plotly.js.tgz + + - name: Test plot-schema.json diff + run: diff --unified --color dist/plot-schema.json test/plot-schema.json + + publish-dist-node-v22: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + - uses: actions/setup-node@v6 + with: + node-version: '22.14.0' + cache: 'npm' + + - name: Set up build environment + run: .github/scripts/env_build.sh + + - name: Preview CHANGELOG for next release (only on master) + if: github.ref == 'refs/heads/master' + run: npm run use-draftlogs && git --no-pager diff --color-words CHANGELOG.md || true + + - name: Set draft version in package.json + run: | + node --eval "var fs = require('fs'); var inOut = './package.json'; var data = JSON.parse(fs.readFileSync(inOut)); var a = process.argv; data.version = a[a.length - 1].replace('v', ''); fs.writeFileSync(inOut, JSON.stringify(data, null, 2) + '\n');" $(git describe) + + - name: View package.json diff between previous and next releases + run: git --no-pager diff --color-words tags/$(git describe --tags --abbrev=0) package.json || true + + - name: Build dist/ + run: npm run build + + - uses: actions/upload-artifact@v7 + with: + name: dist-node22 + retention-days: 7 + path: dist/ + + - name: View dist/README.md diff between previous and next releases + run: git --no-pager diff --color-words tags/$(git describe --tags --abbrev=0) dist/README.md || true + + - name: Preview plot-schema diff (only on master) + if: github.ref == 'refs/heads/master' + run: git --no-pager diff tags/$(git describe --tags --abbrev=0) dist/plot-schema.json || true + + - name: Pack tarball + run: | + npm pack + version=$(node --eval "console.log(require('./package.json').version)") + mv plotly.js-$version.tgz plotly.js.tgz + + - uses: actions/upload-artifact@v7 + with: + name: tarball-node22 + retention-days: 7 + path: plotly.js.tgz + + - name: Test plot-schema.json diff + run: diff --unified --color dist/plot-schema.json test/plot-schema.json + + test-stackgl-bundle: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Set up build environment + run: cd stackgl_modules && npm ci + + - name: Bundle bundle-stackgl/index.js + run: cd stackgl_modules && cp index.js INDEX.js && npm run bundle-stackgl + + - name: Test stackgl_modules/index.js diff + run: diff --unified --color stackgl_modules/INDEX.js stackgl_modules/index.js + + - uses: actions/upload-artifact@v7 + if: failure() + with: + name: stackgl-bundle + retention-days: 7 + path: stackgl_modules/index.js + + test-topojson-build: + if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Set up build environment + run: cd topojson && npm ci + + - name: Build topojson + run: cd topojson && mv dist dist_backup && npm run build + + - name: Compare existing files with newly built files + run: diff -qr topojson/dist topojson/dist_backup + + - uses: actions/upload-artifact@v7 + if: failure() + with: + name: topojson-dist + retention-days: 7 + path: topojson/dist/ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 93caad4a454..00000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,39 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs - -name: No CI Test - -on: push - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.x] - - steps: - - uses: browser-actions/setup-chrome@v2 - id: setup-chrome - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - name: Set Chrome binary path - run: echo "CHROME_BIN=${{ steps.setup-chrome.outputs.chrome-path }}" >> $GITHUB_ENV - - name: Verify Chrome version - run: | - echo "Chrome path: $CHROME_BIN" - $CHROME_BIN --version - - run: ls - - run: npm run pretest - - run: npm ci - - run: npm run cibuild - - name: Run noCI tests - uses: coactions/setup-xvfb@v1 - with: - run: ./tasks/noci_test.sh jasmine diff --git a/biome.json b/biome.json index 24635690072..b176cb37767 100644 --- a/biome.json +++ b/biome.json @@ -11,6 +11,7 @@ "**/tasks/**", "**/devtools/**", "**/topojson/**", + "**/.github/**", "!**/test/plot-schema.json", "!**/dist", "!**/stackgl_modules", diff --git a/package-lock.json b/package-lock.json index 02fc4416104..dcb50905f38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -104,7 +104,7 @@ "minify-stream": "^2.1.0", "npm-link-check": "^5.0.1", "open": "^8.4.2", - "pixelmatch": "^5.3.0", + "pixelmatch": "^7.1.0", "prepend-file": "^2.0.1", "prettysize": "^2.0.0", "raw-loader": "^4.0.2", @@ -8208,12 +8208,13 @@ } }, "node_modules/pixelmatch": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", - "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.1.0.tgz", + "integrity": "sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==", "dev": true, + "license": "ISC", "dependencies": { - "pngjs": "^6.0.0" + "pngjs": "^7.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" @@ -8229,12 +8230,13 @@ } }, "node_modules/pngjs": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", - "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12.13.0" + "node": ">=14.19.0" } }, "node_modules/point-in-polygon": { diff --git a/package.json b/package.json index 04f32177e6e..1c8e1ed6fab 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "pretest": "node tasks/pretest.js", "test-jasmine": "karma start test/jasmine/karma.conf.js", "test-mock": "node tasks/test_mock.mjs", - "test-image": "node test/image/compare_pixels_test.js", + "test-image": "node test/image/compare_pixels_test.mjs", "test-export": "node test/image/export_test.js", "test-syntax": "node tasks/test_syntax.js && npm run find-strings -- --no-output", "test-bundle": "node tasks/test_bundle.js", @@ -162,7 +162,7 @@ "minify-stream": "^2.1.0", "npm-link-check": "^5.0.1", "open": "^8.4.2", - "pixelmatch": "^5.3.0", + "pixelmatch": "^7.1.0", "prepend-file": "^2.0.1", "prettysize": "^2.0.0", "raw-loader": "^4.0.2", diff --git a/tasks/test_mock.mjs b/tasks/test_mock.mjs index ae9bef8fcdd..2a69eaf7276 100644 --- a/tasks/test_mock.mjs +++ b/tasks/test_mock.mjs @@ -1,240 +1,246 @@ +import fs from 'fs'; import minimist from 'minimist'; +import os from 'os'; import path from 'path'; -import fs from 'fs'; +import { fileURLToPath } from 'url'; +import { isMainThread, parentPort, Worker, workerData } from 'worker_threads'; -import plotlyNode from './util/plotly_node.mjs'; -var Plotly = plotlyNode('build/plotly.js'); +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const pathToRoot = path.join(__dirname, '..'); +const pathToMocks = path.join(pathToRoot, 'test', 'image', 'mocks'); -import { fileURLToPath } from 'url'; -var __dirname = path.dirname(fileURLToPath(import.meta.url)); +const disallowList = new Set([ + // has contourcarpet See https://github.com/plotly/plotly.js/issues/5669 + 'airfoil', + 'h-colorbar_airfoil', + 'cheater', + 'cheater_constraint_greater_than', + 'cheater_constraint_greater_than_with_hill', + 'cheater_constraint_greater_than_with_valley', + 'cheater_constraint_inner_range', + 'cheater_constraint_inner_range_hi_top', + 'cheater_constraint_inner_range_hi_top_with_hill', + 'cheater_constraint_inner_range_hi_top_with_valley', + 'cheater_constraint_inner_range_lo_top', + 'cheater_constraint_inner_range_lo_top_with_hill', + 'cheater_constraint_inner_range_lo_top_with_valley', + 'cheater_constraint_inner_range_with_hill', + 'cheater_constraint_inner_range_with_valley', + 'cheater_constraint_less_than', + 'cheater_constraint_less_than_with_hill', + 'cheater_constraint_less_than_with_valley', + 'cheater_constraint_outer_range', + 'cheater_constraint_outer_range_hi_top', + 'cheater_constraint_outer_range_hi_top_with_hill', + 'cheater_constraint_outer_range_hi_top_with_valley', + 'cheater_constraint_outer_range_lo_top', + 'cheater_constraint_outer_range_lo_top_with_hill', + 'cheater_constraint_outer_range_lo_top_with_valley', + 'cheater_constraint_outer_range_with_hill', + 'cheater_constraint_outer_range_with_valley', + 'cheater_constraints', + 'cheater_contour', + 'cheater_fully_filled', + 'cheater_smooth', -var pathToRoot = path.join(__dirname, '..'); -var pathToMocks = path.join(pathToRoot, 'test', 'image', 'mocks'); + // other + '1', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '21', + '22', + '23', + '24', + '25', + '26', + '27', + '28', + '29', + '30', + '31', + '32', + 'annotations', + 'annotations-autorange', + 'axes_labels', + 'candlestick_double-y-axis', + 'candlestick_rangeslider_thai', + 'category-autorange', + 'contour_match_edges', + 'dendrogram', + 'error_bar_style', + 'fake_violins', + 'fonts', + 'font-variant-bar', + 'font-weight-bar', + 'geo_africa-insets', + 'gl2d_10', + 'gl2d_12', + 'gl2d_14', + 'gl2d_17', + 'gl2d_annotations', + 'gl2d_axes_labels', + 'gl2d_fill_trace_tozero_order', + 'gl2d_fonts', + 'gl2d_layout_image', + 'gl2d_marker_coloraxis', + 'gl2d_rgb_dont_accept_alpha_scattergl', + 'gl2d_scatter-marker-line-colorscales', + 'gl2d_scatter-subplot-panel', + 'gl2d_shape_line', + 'gl2d_text_chart_basic', + 'gl2d_text_chart_single-string', + 'gl2d_text_chart_styling', + 'gl2d_texttemplate', + 'gl3d_bunny', + 'gl3d_bunny-hull', + 'gl3d_coloraxes', + 'gl3d_contour-lines', + 'gl3d_contour-lines2', + 'gl3d_convex-hull', + 'gl3d_cufflinks', + 'gl3d_directions-volume1', + 'gl3d_ibm-plot', + 'gl3d_line-colorscale-with-markers', + 'gl3d_opacity-surface', + 'gl3d_scatter-colorscale-marker', + 'gl3d_scatter3d-align-texts', + 'gl3d_surface_opacity-and-opacityscale', + 'gl3d_surface_opacityscale_contour', + 'gl3d_surface-heatmap-treemap_transparent-colorscale', + 'gl3d_surface-lighting', + 'gl3d_traces-with-legend', + 'gl3d_traces-with-opacity', + 'gl3d_volume_multiple-traces', + 'gl3d_z-range', + 'glpolar_scatter', + 'heatmap_small_aspect-ratio', + 'histogram_errorbars_inherit_color', + 'indicator_attrs', + 'indicator_bignumber', + 'indicator_bullet', + 'indicator_datacard', + 'indicator_datacard2', + 'indicator_gauge', + 'indicator_scatter', + 'japanese', + 'layout_image', + 'layout_metatext', + 'legend_horizontal', + 'legend_horizontal_autowrap', + 'legend-constant-itemsizing', + 'matching-missing-axes', + 'mathjax', + 'ohlc_first', + 'pattern_bars', + 'pattern_fgcolor_overlay_fillmode', + 'pattern_with_colorscale', + 'petrophysics', + 'plot_types', + 'polar_blank', + 'polar_dates', + 'range_slider_box', + 'shapes_fixed_size', + 'splom_ragged-via-axes', + 'stacked_area_duplicates', + 'table_plain_birds', + 'table_wrapped_birds', + 'text_chart_basic', + 'text_chart_single-string', + 'text_chart_styling', + 'texttemplate', + 'texttemplate_scatter', + 'titles-avoid-labels', + 'trace_metatext', + 'updatemenus', + 'violin_non-linear', + 'violin_ridgeplot', + 'violin_style', + 'waterfall_funnel_texttemplate_date', + 'yaxis-over-yaxis2', + 'yignbu_heatmap', + 'yiorrd_heatmap' +]); -var list = []; +function validate(Plotly, name) { + const filename = path.join(pathToMocks, `${name}.json`); + const fig = JSON.parse(fs.readFileSync(filename)); + const out = Plotly.validate(fig.data, fig.layout); -// command line options -var args = minimist(process.argv.slice(2), {}); -if(args._.length) { - // test listed mock(s) - list = args._; -} else { - // no mock listed, test all excluding the black list - list = fs.readdirSync(pathToMocks) - .filter(function(e) { return e.indexOf('.json') !== -1; }) - .map(function(e) { return e.replace('.json', ''); }) - .filter(notBlackListed); + if (!out) { + if (out !== undefined) { + console.error(`Expected ${out} to be undefined`); + return false; + } + return true; + } + + for (const e of out) { + if (!['dynamic', 'invisible'].includes(e.code) && e.path.at(-1) !== 'coloraxis') { + console.error('Expected false to be true'); + console.log('file:', name); + console.log(JSON.stringify(out, null, 2)); + return false; + } + } + return true; } -var fail; -var failedMocks = []; +if (isMainThread) { + const args = minimist(process.argv.slice(2), {}); + const list = args._.length + ? args._ + : fs + .readdirSync(pathToMocks) + .filter((e) => e.endsWith('.json')) + .map((e) => e.replace('.json', '')) + .filter((e) => !disallowList.has(e)); -for(var i = 0; i < list.length; i++) { - var name = list[i]; - console.log('validating ' + name); + const numWorkers = Math.min(args.workers || os.cpus().length, list.length); + const chunkSize = Math.ceil(list.length / numWorkers); + const promises = []; - var filename = path.join(pathToMocks, name + '.json'); - var fig = JSON.parse(fs.readFileSync(filename)); - var out = Plotly.validate(fig.data, fig.layout); + console.log(`Validating ${list.length} mocks across ${numWorkers} workers...`); - fail = false; - assert(name, out); - if(fail) failedMocks.push(name); -} + for (let i = 0; i < numWorkers; i++) { + const chunk = list.slice(i * chunkSize, (i + 1) * chunkSize); + if (chunk.length === 0) continue; -if(failedMocks.length) { - var error = 'Failed at ' + JSON.stringify({mocks: failedMocks}, null, 2); - throw error; -} + promises.push( + new Promise((resolve, reject) => { + const worker = new Worker(new URL(import.meta.url), { + workerData: { mocks: chunk } + }); + worker.on('message', resolve); + worker.on('error', reject); + }) + ); + } + + const results = await Promise.all(promises); + const failedMocks = results.flat(); -function expectToBe(actual, expected) { - if(actual !== expected) { - console.error('Expected ' + actual + ' to be ' + expected); - fail = true; + if (failedMocks.length) { + throw `Failed at ${JSON.stringify({ mocks: failedMocks }, null, 2)}`; } -} -function assert(name, v) { - var success = true; - if(!v) { - expectToBe(v, undefined); - if(v !== undefined) success = false; - } else { - v.forEach(function(e) { - var condition = ( - e.code === 'invisible' || - e.code === 'dynamic' || - e.path[e.path.length - 1] === 'coloraxis' - ); - expectToBe(condition, true); // we accept invisible, dynamic and coloraxis for now - if(!condition) { - console.log('file:', name); - console.log(JSON.stringify(v, null, 2)); - success = false; - return success; - } - }); + console.log('All mocks validated successfully.'); +} else { + const { default: plotlyNode } = await import('./util/plotly_node.mjs'); + const Plotly = plotlyNode('build/plotly.js'); + const { mocks } = workerData; + const failedMocks = []; + + for (const name of mocks) { + console.log(`validating ${name}`); + if (!validate(Plotly, name)) failedMocks.push(name); } - return success; -} -function notBlackListed(name) { - return [ - // has contourcarpet See https://github.com/plotly/plotly.js/issues/5669 - 'airfoil', - 'h-colorbar_airfoil', - 'cheater', - 'cheater_constraint_greater_than', - 'cheater_constraint_greater_than_with_hill', - 'cheater_constraint_greater_than_with_valley', - 'cheater_constraint_inner_range', - 'cheater_constraint_inner_range_hi_top', - 'cheater_constraint_inner_range_hi_top_with_hill', - 'cheater_constraint_inner_range_hi_top_with_valley', - 'cheater_constraint_inner_range_lo_top', - 'cheater_constraint_inner_range_lo_top_with_hill', - 'cheater_constraint_inner_range_lo_top_with_valley', - 'cheater_constraint_inner_range_with_hill', - 'cheater_constraint_inner_range_with_valley', - 'cheater_constraint_less_than', - 'cheater_constraint_less_than_with_hill', - 'cheater_constraint_less_than_with_valley', - 'cheater_constraint_outer_range', - 'cheater_constraint_outer_range_hi_top', - 'cheater_constraint_outer_range_hi_top_with_hill', - 'cheater_constraint_outer_range_hi_top_with_valley', - 'cheater_constraint_outer_range_lo_top', - 'cheater_constraint_outer_range_lo_top_with_hill', - 'cheater_constraint_outer_range_lo_top_with_valley', - 'cheater_constraint_outer_range_with_hill', - 'cheater_constraint_outer_range_with_valley', - 'cheater_constraints', - 'cheater_contour', - 'cheater_fully_filled', - 'cheater_smooth', - - // other - '1', - '11', - '12', - '13', - '14', - '15', - '16', - '17', - '18', - '19', - '21', - '22', - '23', - '24', - '25', - '26', - '27', - '28', - '29', - '30', - '31', - '32', - 'annotations', - 'annotations-autorange', - 'axes_labels', - 'candlestick_double-y-axis', - 'candlestick_rangeslider_thai', - 'category-autorange', - 'contour_match_edges', - 'dendrogram', - 'error_bar_style', - 'fake_violins', - 'fonts', - 'font-variant-bar', - 'font-weight-bar', - 'geo_africa-insets', - 'gl2d_10', - 'gl2d_12', - 'gl2d_14', - 'gl2d_17', - 'gl2d_annotations', - 'gl2d_axes_labels', - 'gl2d_fill_trace_tozero_order', - 'gl2d_fonts', - 'gl2d_layout_image', - 'gl2d_marker_coloraxis', - 'gl2d_rgb_dont_accept_alpha_scattergl', - 'gl2d_scatter-marker-line-colorscales', - 'gl2d_scatter-subplot-panel', - 'gl2d_shape_line', - 'gl2d_text_chart_basic', - 'gl2d_text_chart_single-string', - 'gl2d_text_chart_styling', - 'gl2d_texttemplate', - 'gl3d_bunny', - 'gl3d_bunny-hull', - 'gl3d_coloraxes', - 'gl3d_contour-lines', - 'gl3d_contour-lines2', - 'gl3d_convex-hull', - 'gl3d_cufflinks', - 'gl3d_directions-volume1', - 'gl3d_ibm-plot', - 'gl3d_line-colorscale-with-markers', - 'gl3d_opacity-surface', - 'gl3d_scatter-colorscale-marker', - 'gl3d_scatter3d-align-texts', - 'gl3d_surface_opacity-and-opacityscale', - 'gl3d_surface_opacityscale_contour', - 'gl3d_surface-heatmap-treemap_transparent-colorscale', - 'gl3d_surface-lighting', - 'gl3d_traces-with-legend', - 'gl3d_traces-with-opacity', - 'gl3d_volume_multiple-traces', - 'gl3d_z-range', - 'glpolar_scatter', - 'heatmap_small_aspect-ratio', - 'histogram_errorbars_inherit_color', - 'indicator_attrs', - 'indicator_bignumber', - 'indicator_bullet', - 'indicator_datacard', - 'indicator_datacard2', - 'indicator_gauge', - 'indicator_scatter', - 'japanese', - 'layout_image', - 'layout_metatext', - 'legend_horizontal', - 'legend_horizontal_autowrap', - 'legend-constant-itemsizing', - 'matching-missing-axes', - 'mathjax', - 'ohlc_first', - 'pattern_bars', - 'pattern_fgcolor_overlay_fillmode', - 'pattern_with_colorscale', - 'petrophysics', - 'plot_types', - 'polar_blank', - 'polar_dates', - 'range_slider_box', - 'shapes_fixed_size', - 'splom_ragged-via-axes', - 'stacked_area_duplicates', - 'table_plain_birds', - 'table_wrapped_birds', - 'text_chart_basic', - 'text_chart_single-string', - 'text_chart_styling', - 'texttemplate', - 'texttemplate_scatter', - 'titles-avoid-labels', - 'trace_metatext', - 'updatemenus', - 'violin_non-linear', - 'violin_ridgeplot', - 'violin_style', - 'waterfall_funnel_texttemplate_date', - 'yaxis-over-yaxis2', - 'yignbu_heatmap', - 'yiorrd_heatmap' - ].indexOf(name) === -1; + parentPort.postMessage(failedMocks); } diff --git a/test/image/baselines/zz-geo_choropleth-egypt-sudan.png b/test/image/baselines/geo_choropleth-egypt-sudan.png similarity index 100% rename from test/image/baselines/zz-geo_choropleth-egypt-sudan.png rename to test/image/baselines/geo_choropleth-egypt-sudan.png diff --git a/test/image/baselines/geo_choropleth-usa-location-names.png b/test/image/baselines/geo_choropleth-usa-location-names.png new file mode 100644 index 00000000000..eeeadd84555 Binary files /dev/null and b/test/image/baselines/geo_choropleth-usa-location-names.png differ diff --git a/test/image/baselines/zz-label-spacing.png b/test/image/baselines/label-spacing.png similarity index 100% rename from test/image/baselines/zz-label-spacing.png rename to test/image/baselines/label-spacing.png diff --git a/test/image/baselines/zz-legend-vertical-maxheight.png b/test/image/baselines/legend-vertical-maxheight.png similarity index 100% rename from test/image/baselines/zz-legend-vertical-maxheight.png rename to test/image/baselines/legend-vertical-maxheight.png diff --git a/test/image/baselines/zz-minorloglabels.png b/test/image/baselines/minorloglabels.png similarity index 100% rename from test/image/baselines/zz-minorloglabels.png rename to test/image/baselines/minorloglabels.png diff --git a/test/image/baselines/zz-pattern_bars-path.png b/test/image/baselines/pattern_bars-path.png similarity index 100% rename from test/image/baselines/zz-pattern_bars-path.png rename to test/image/baselines/pattern_bars-path.png diff --git a/test/image/baselines/zz-pie-slice-legend.png b/test/image/baselines/pie-slice-legend.png similarity index 100% rename from test/image/baselines/zz-pie-slice-legend.png rename to test/image/baselines/pie-slice-legend.png diff --git a/test/image/baselines/zz-pie-slice-legend2.png b/test/image/baselines/pie-slice-legend2.png similarity index 100% rename from test/image/baselines/zz-pie-slice-legend2.png rename to test/image/baselines/pie-slice-legend2.png diff --git a/test/image/baselines/zz-pie-slice-legend3.png b/test/image/baselines/pie-slice-legend3.png similarity index 100% rename from test/image/baselines/zz-pie-slice-legend3.png rename to test/image/baselines/pie-slice-legend3.png diff --git a/test/image/baselines/zz-scatter_marker_line_dash.png b/test/image/baselines/scatter_marker_line_dash.png similarity index 100% rename from test/image/baselines/zz-scatter_marker_line_dash.png rename to test/image/baselines/scatter_marker_line_dash.png diff --git a/test/image/baselines/zz_shapes_clipping_double_digit_subplots.png b/test/image/baselines/shapes_clipping_double_digit_subplots.png similarity index 100% rename from test/image/baselines/zz_shapes_clipping_double_digit_subplots.png rename to test/image/baselines/shapes_clipping_double_digit_subplots.png diff --git a/test/image/baselines/zz-tickson_boundaries_ticklabelposition.png b/test/image/baselines/tickson_boundaries_ticklabelposition.png similarity index 100% rename from test/image/baselines/zz-tickson_boundaries_ticklabelposition.png rename to test/image/baselines/tickson_boundaries_ticklabelposition.png diff --git a/test/image/baselines/zzz_zerolinelayer_above.png b/test/image/baselines/zerolinelayer_above.png similarity index 100% rename from test/image/baselines/zzz_zerolinelayer_above.png rename to test/image/baselines/zerolinelayer_above.png diff --git a/test/image/baselines/zzz_zerolinelayer_below.png b/test/image/baselines/zerolinelayer_below.png similarity index 100% rename from test/image/baselines/zzz_zerolinelayer_below.png rename to test/image/baselines/zerolinelayer_below.png diff --git a/test/image/compare_pixels_collections.json b/test/image/compare_pixels_collections.json new file mode 100644 index 00000000000..234eb781609 --- /dev/null +++ b/test/image/compare_pixels_collections.json @@ -0,0 +1,21 @@ +{ + "compare_disallow": [ + "map_angles", + "map_fonts-supported-open-sans-weight", + "map_fonts-supported-open-sans", + "map_layers", + "map_predefined-styles2", + "map_scattercluster", + "map_stamen-style" + ], + "compare_mathjax3": [ + "legend_mathjax_title_and_items", + "mathjax", + "parcats_grid_subplots", + "table_latex_multitrace_scatter", + "table_plain_birds", + "table_wrapped_birds", + "ternary-mathjax", + "ternary-mathjax-title-place-subtitle" + ] +} diff --git a/test/image/compare_pixels_test.js b/test/image/compare_pixels_test.js deleted file mode 100644 index 574e5edf51f..00000000000 --- a/test/image/compare_pixels_test.js +++ /dev/null @@ -1,204 +0,0 @@ -var minimist = require('minimist'); -var pixelmatch = require('pixelmatch'); -var PNG = require('pngjs').PNG; -var fs = require('fs'); - -var common = require('../../tasks/util/common'); -var getMockList = require('./assets/get_mock_list'); -var getImagePaths = require('./assets/get_image_paths'); - -/** - * Image pixel comparison test script. - * - * Called by `tasks/test_image.sh in `npm run test-image`. - * - * CLI arguments: - * - * 1. 'pattern' : glob determining which mock(s) are to be tested - * - * Examples: - * - * Run all tests: - * - * npm run test-image - * - * Run the 'contour_nolines' test: - * - * npm run test-image -- contour_nolines - * - * Run all gl3d image test - * - * npm run test-image -- gl3d_* - * - */ - -var argv = minimist(process.argv.slice(2), {}); - -// If no pattern is provided, all mocks are compared -if(argv._.length === 0) { - argv._.push(''); -} - -// Build list of mocks to compare -var allMockList = []; -var mathjax3; -var virtualWebgl = false; -argv._.forEach(function(pattern) { - if(pattern === 'mathjax3') { - mathjax3 = true; - } else if(pattern === 'virtual-webgl') { - virtualWebgl = true; - allMockList = getMockList(''); - } else { - var mockList = getMockList(pattern); - - if(mockList.length === 0) { - throw 'No mocks found with pattern ' + pattern; - } - - allMockList = allMockList.concat(mockList); - } -}); - -var blacklist = [ - 'map_angles', - 'map_stamen-style', - 'map_predefined-styles2', - 'map_scattercluster', - 'map_fonts-supported-open-sans', - 'map_fonts-supported-open-sans-weight', - 'map_layers', -]; - -if(virtualWebgl) { - allMockList = allMockList.filter(function(a) { - return a.slice(0, 2) === 'gl'; - }); -} - -if(mathjax3) { - allMockList = [ - 'legend_mathjax_title_and_items', - 'mathjax', - 'parcats_grid_subplots', - 'table_latex_multitrace_scatter', - 'table_plain_birds', - 'table_wrapped_birds', - 'ternary-mathjax', - 'ternary-mathjax-title-place-subtitle', - ]; -} - -// To get rid of duplicates -function unique(value, index, self) { - return self.indexOf(value) === index; -} -allMockList = allMockList.filter(unique); - -var skipped = []; -var failed = []; -var fail = function(mockName) { - if(failed.indexOf(mockName) === -1) { - failed.push(mockName); - } -}; - -for(var i = 0; i < allMockList.length; i++) { - var mockName = allMockList[i]; - - // skip blacklist - if(blacklist.indexOf(mockName) !== -1) continue; - - var flakyMap = [ - // more flaky - 'map_density0-legend', - 'map_osm-style', - 'map_predefined-styles1', - 'map_predefined-styles2', - ].indexOf(mockName) !== -1; - - var otherFlaky = [ - // list flaky mocks other than maps: - 'gl3d_bunny-hull' - ].indexOf(mockName) !== -1; - - var threshold = - flakyMap ? 1 : - otherFlaky ? 0.15 : - 0; - - if(mathjax3) mockName = 'mathjax3___' + mockName; - - var imagePaths = getImagePaths(mockName); - var base = imagePaths.baseline; - var test = imagePaths.test; - - if(!common.doesFileExist(test) && !mathjax3) { - console.log('- skip:', mockName); - skipped.push(mockName); - continue; - } - console.log('+ test:', mockName); - - var img0 = PNG.sync.read(fs.readFileSync(base)); - var img1 = PNG.sync.read(fs.readFileSync(test)); - var s0, s1, key; - - key = 'width'; - s0 = img0[key]; - s1 = img0[key]; - if(s0 !== s1) { - console.error(key + 's do not match: ' + s0 + ' vs ' + s1); - fail(mockName); - } - - key = 'height'; - s0 = img0[key]; - s1 = img0[key]; - if(s0 !== s1) { - console.error(key + 's do not match: ' + s0 + ' vs ' + s1); - fail(mockName); - } - - var width = img0.width; - var height = img0.height; - - var diff = new PNG({ - width: width, - height: height - }); - - if(virtualWebgl) { - threshold = Math.max(0.4, threshold); - if([ - 'gl3d_ibm-plot', - 'gl3d_isosurface_2surfaces-checker_spaceframe', - 'gl3d_opacity-scaling-spikes', - 'gl3d_cone-wind', - 'gl3d_isosurface_math', - 'gl3d_scatter3d-blank-text', - 'gl3d_mesh3d_surface3d_scatter3d_line3d_error3d_log_reversed_ranges' - ].indexOf(mockName) !== -1) threshold = 0.7; - } - - var numDiffPixels = pixelmatch(img0.data, img1.data, diff.data, width, height, { - threshold: threshold - }); - - if(numDiffPixels) { - fs.writeFileSync(imagePaths.diff, PNG.sync.write(diff)); - - console.error('pixels do not match: ' + numDiffPixels); - fail(mockName); - } else { - // remove when identical - fs.unlinkSync(imagePaths.test); - } -} - -if(failed.length || skipped.length) { - throw JSON.stringify({ - failed: failed, - skipped: skipped - }, null, 2); -} diff --git a/test/image/compare_pixels_test.mjs b/test/image/compare_pixels_test.mjs new file mode 100644 index 00000000000..d23d64458b8 --- /dev/null +++ b/test/image/compare_pixels_test.mjs @@ -0,0 +1,173 @@ +import fs from 'fs'; +import minimist from 'minimist'; +import path from 'path'; +import pixelmatch from 'pixelmatch'; +import { PNG } from 'pngjs'; +import { fileURLToPath } from 'url'; +import common from '../../tasks/util/common.js'; +import constants from '../../tasks/util/constants.js'; +import getImagePaths from './assets/get_image_paths.js'; +import getMockList from './assets/get_mock_list.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +fs.mkdirSync(constants.pathToTestImagesDiff, { recursive: true }); + +/** + * Image pixel comparison test script. + * + * Called by `tasks/test_image.sh in `npm run test-image`. + * + * CLI arguments: + * + * 1. 'pattern' : glob determining which mock(s) are to be tested + * + * Examples: + * + * Run all tests: + * + * npm run test-image + * + * Run the 'contour_nolines' test: + * + * npm run test-image -- contour_nolines + * + * Run all gl3d image test + * + * npm run test-image -- gl3d_* + * + */ + +const argv = minimist(process.argv.slice(2), {}); + +// If no pattern is provided, all mocks are compared +if (argv._.length === 0) argv._.push(''); + +// Build list of mocks to compare +let allMockList = []; +let mathjax3 = false; +let virtualWebgl = false; +argv._.forEach((pattern) => { + if (pattern === 'mathjax3') { + mathjax3 = true; + } else if (pattern === 'virtual-webgl') { + virtualWebgl = true; + allMockList = getMockList(''); + } else { + const mockList = getMockList(pattern); + if (mockList.length === 0) throw 'No mocks found with pattern ' + pattern; + + allMockList = allMockList.concat(mockList); + } +}); + +const skipped = new Set(); +const failed = new Set(); +// TODO: In Node 20+, replace with: import collections from './compare_pixels_collections.json' with { type: 'json' }; +const collectionsPath = path.join(__dirname, 'compare_pixels_collections.json'); +const collections = JSON.parse(fs.readFileSync(collectionsPath)); +const disallowList = new Set(collections.compare_disallow); +const flakyList = new Set(['gl3d_bunny-hull']); +const flakyListMaps = new Set([ + // more flaky + 'map_density0-legend', + 'map_osm-style', + 'map_predefined-styles1', + 'map_predefined-styles2' +]); +if (virtualWebgl) { + allMockList = allMockList.filter((a) => a.startsWith('gl') || a.startsWith('map')); +} + +if (mathjax3) { + allMockList = collections.compare_mathjax3; +} +allMockList = new Set(allMockList); + +for (let mockName of allMockList) { + if (disallowList.has(mockName)) continue; + + let threshold; + if (flakyListMaps.has(mockName)) threshold = 1; + else if (flakyList.has(mockName)) threshold = 0.15; + else threshold = 0; + + if (mathjax3) mockName = 'mathjax3___' + mockName; + if (virtualWebgl) mockName = 'virtual-webgl___' + mockName; + + const { baseline: base, test, diff } = getImagePaths(mockName); + + if (!common.doesFileExist(test) && !mathjax3) { + console.log('- skip:', mockName); + skipped.add(mockName); + continue; + } + console.log('+ test:', mockName); + + try { + if (!common.doesFileExist(base)) { + console.error('baseline image missing'); + fs.copyFileSync(test, diff); + failed.add(mockName); + continue; + } + + const img0 = PNG.sync.read(fs.readFileSync(base)); + const img1 = PNG.sync.read(fs.readFileSync(test)); + let dimensionMismatch = false; + for (const key of ['height', 'width']) { + const length0 = img0[key]; + const length1 = img1[key]; + if (length0 !== length1) { + console.error(key + 's do not match: ' + length0 + ' vs ' + length1); + dimensionMismatch = true; + } + } + + if (dimensionMismatch) { + fs.copyFileSync(test, diff); + failed.add(mockName); + continue; + } + + const { height, width } = img0; + const imageDiff = new PNG({ width, height }); + const numDiffPixels = pixelmatch(img0.data, img1.data, imageDiff.data, width, height, { threshold }); + + if (numDiffPixels) { + fs.writeFileSync(diff, PNG.sync.write(imageDiff)); + console.error('pixels do not match: ' + numDiffPixels); + failed.add(mockName); + } else { + // remove when identical + fs.unlinkSync(test); + } + } catch (e) { + console.error('error comparing ' + mockName + ':', e); + failed.add(mockName); + } +} + +// Debug: list contents of diff folder +console.log('\n=== build/test_images/ ==='); +try { + const testFiles = fs.readdirSync(constants.pathToTestImages); + console.log(testFiles.length + ' files:', testFiles.join(', ')); +} catch { console.log('(empty or missing)'); } + +console.log('=== build/test_images_diff/ ==='); +try { + const diffFiles = fs.readdirSync(constants.pathToTestImagesDiff); + console.log(diffFiles.length + ' files:', diffFiles.join(', ')); +} catch { console.log('(empty or missing)'); } + +if (failed.size || skipped.size) { + throw JSON.stringify( + { + failed: Array.from(failed), + skipped: Array.from(skipped) + }, + null, + 2 + ); +} diff --git a/test/image/make_baseline.py b/test/image/make_baseline.py index 01dc836da76..ff0b600df09 100644 --- a/test/image/make_baseline.py +++ b/test/image/make_baseline.py @@ -1,134 +1,160 @@ +import asyncio +import json +import kaleido import os import sys -import json -import plotly.io as pio from convert_b64 import arraysToB64 args = [] -if len(sys.argv) == 2 : +if len(sys.argv) == 2: args = sys.argv[1].split() -elif len(sys.argv) > 1 : +elif len(sys.argv) > 1: args = sys.argv root = os.getcwd() -virtual_webgl = os.path.join(root, 'node_modules', 'virtual-webgl', 'src', 'virtual-webgl.js') -plotlyjs = os.path.join(root, 'build', 'plotly.js') -plotlyjs_with_virtual_webgl = os.path.join(root, 'build', 'plotly_with_virtual-webgl.js') +virtual_webgl = os.path.join( + root, "node_modules", "virtual-webgl", "src", "virtual-webgl.js" +) +plotlyjs = os.path.join(root, "build", "plotly.js") +plotlyjs_with_virtual_webgl = os.path.join( + root, "build", "plotly_with_virtual-webgl.js" +) -dirIn = os.path.join(root, 'test', 'image', 'mocks') -dirOut = os.path.join(root, 'build', 'test_images') +topojson = "file://" + os.path.join(root, "topojson", "dist") +dirIn = os.path.join(root, "test", "image", "mocks") +dirOut = os.path.join(root, "build", "test_images") -# N.B. equal is the falg to write to baselines not test_images +# N.B. equal is the flag to write to baselines not test_images -if '=' in args : - args = args[args.index('=') + 1:] - dirOut = os.path.join(root, 'test', 'image', 'baselines') +if "=" in args: + args = args[args.index("=") + 1 :] + dirOut = os.path.join(root, "test", "image", "baselines") -if 'mathjax3=' in sys.argv : - dirOut = os.path.join(root, 'test', 'image', 'baselines') +if "mathjax3=" in sys.argv: + dirOut = os.path.join(root, "test", "image", "baselines") -print('output to', dirOut) +print("output to", dirOut) mathjax_version = 2 -if 'mathjax3' in sys.argv or 'mathjax3=' in sys.argv : +mathjax = None +if "mathjax3" in sys.argv or "mathjax3=" in sys.argv: # until https://github.com/plotly/Kaleido/issues/124 is addressed # we are uanble to use local mathjax v3 installed in node_modules # for now let's download it from the internet: - pio.kaleido.scope.mathjax = 'https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-svg.js' + mathjax = "https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-svg.js" + mathjax_version = 3 - print('Kaleido using MathJax v3') + print("Kaleido using MathJax v3") -virtual_webgl_version = 0 # i.e. virtual-webgl is not used -if 'virtual-webgl' in sys.argv or 'virtual-webgl=' in sys.argv : +virtual_webgl_version = 0 # i.e. virtual-webgl is not used +if "virtual-webgl" in sys.argv or "virtual-webgl=" in sys.argv: virtual_webgl_version = 1 - print('using virtual-webgl for WebGL v1') + print("using virtual-webgl for WebGL v1") - with open(plotlyjs_with_virtual_webgl, 'w') as fileOut: + with open(plotlyjs_with_virtual_webgl, "w") as fileOut: for filename in [virtual_webgl, plotlyjs]: - with open(filename, 'r') as fileIn: + with open(filename, "r") as fileIn: for line in fileIn: fileOut.write(line) plotlyjs = plotlyjs_with_virtual_webgl -pio.kaleido.scope.plotlyjs = plotlyjs -pio.kaleido.scope.topojson = "file://" + os.path.join(root, 'topojson', 'dist') -pio.templates.default = 'none' - -ALL_MOCKS = [os.path.splitext(a)[0] for a in os.listdir(dirIn) if a.endswith('.json')] +ALL_MOCKS = [os.path.splitext(a)[0] for a in os.listdir(dirIn) if a.endswith(".json")] ALL_MOCKS.sort() -if len(args) > 0 : +if len(args) > 0: allNames = [a for a in args if a in ALL_MOCKS] -else : +else: allNames = ALL_MOCKS -# unable to generate baselines for the following mocks -blacklist = [ - 'map_stamen-style', - 'map_predefined-styles2', - 'map_scattercluster', - 'map_fonts-supported-open-sans', - 'map_fonts-supported-open-sans-weight', - 'map_layers', -] -allNames = [a for a in allNames if a not in blacklist] - -if len(allNames) == 0 : - print('error: Nothing to create!') +with open(os.path.join(root, "test", "image", "compare_pixels_collections.json"), "r") as f: + # unable to generate baselines for the following mocks + disallowList = set(json.load(f)["compare_disallow"]) +allNames = [a for a in allNames if a not in disallowList] + +if len(allNames) == 0: + print("error: Nothing to create!") sys.exit(1) failed = [] -for name in allNames : - outName = name - if mathjax_version == 3 : - outName = 'mathjax3___' + name - - print(outName) - - created = False - - MAX_RETRY = 2 # 1 means retry once - for attempt in range(0, MAX_RETRY + 1) : - with open(os.path.join(dirIn, name + '.json'), 'r') as _in : - fig = json.load(_in) - - width = 700 - height = 500 - if 'layout' in fig : - layout = fig['layout'] - if 'autosize' not in layout or layout['autosize'] != True : - if 'width' in layout : - width = layout['width'] - if 'height' in layout : - height = layout['height'] - - if 'b64' in sys.argv or 'b64=' in sys.argv or 'b64-json' in sys.argv : - newFig = dict() - arraysToB64(fig, newFig) - fig = newFig - if 'b64-json' in sys.argv and attempt == 0 : print(json.dumps(fig, indent = 2)) - - try : - pio.write_image( - fig=fig, - file=os.path.join(dirOut, outName + '.png'), - width=width, - height=height, - validate=False - ) - created = True - except Exception as e : - print(e) - if attempt < MAX_RETRY : - print('retry', attempt + 1, '/', MAX_RETRY) - else : - failed.append(outName) - - if(created) : break - -if len(failed) > 0 : - print('Failed at :') - print(failed) - sys.exit(1) + + +async def make_baselines_async(): + + kopts = dict( + plotlyjs=plotlyjs, + ) + if mathjax is not None: + kopts["mathjax"] = mathjax + + async with kaleido.Kaleido(n=1, **kopts) as k: + for name in allNames: + outName = name + if mathjax_version == 3: + outName = "mathjax3___" + name + if virtual_webgl_version == 1: + outName = "virtual-webgl___" + outName + + print(outName) + + created = False + + MAX_RETRY = 2 # 1 means retry once + for attempt in range(0, MAX_RETRY + 1): + with open(os.path.join(dirIn, name + ".json"), "r") as _in: + fig = json.load(_in) + + width = 700 + height = 500 + if "layout" in fig: + layout = fig["layout"] + if "autosize" not in layout or layout["autosize"] != True: + if "width" in layout: + width = layout["width"] + if "height" in layout: + height = layout["height"] + + if ( + "b64" in sys.argv + or "b64=" in sys.argv + or "b64-json" in sys.argv + ): + newFig = dict() + arraysToB64(fig, newFig) + fig = newFig + if "b64-json" in sys.argv and attempt == 0: + print(json.dumps(fig, indent=2)) + + try: + bytes = await k.calc_fig( + fig, + opts=dict( + format="png", + width=width, + height=height, + ), + topojson=topojson, + ) + filename = os.path.join(dirOut, outName + ".png") + with open(filename, "wb") as f: + f.write(bytes) + created = True + except Exception as e: + print(e) + if attempt < MAX_RETRY: + print("retry", attempt + 1, "/", MAX_RETRY) + else: + failed.append(outName) + + if created: + break + + if len(failed) > 0: + print("Failed at :") + print(failed) + sys.exit(1) + + +if __name__ == "__main__": + asyncio.run(make_baselines_async()) diff --git a/test/image/make_exports.py b/test/image/make_exports.py index 279439af1e4..42774ed9ff0 100644 --- a/test/image/make_exports.py +++ b/test/image/make_exports.py @@ -1,65 +1,82 @@ +import asyncio +import json +import kaleido import os import sys -import json -import plotly.io as pio root = os.getcwd() -dirIn = os.path.join(root, 'test', 'image', 'mocks') -dirOut = os.path.join(root, 'build', 'test_images') +dirIn = os.path.join(root, "test", "image", "mocks") +dirOut = os.path.join(root, "build", "test_images") +os.makedirs(dirOut, exist_ok=True) -pio.templates.default = 'none' -pio.kaleido.scope.plotlyjs = os.path.join(root, 'build', 'plotly.js') +plotlyjs = os.path.join(root, "build", "plotly.js") +topojson = "file://" + os.path.join(root, "topojson", "dist") -allFormats = ['svg', 'jpg', 'jpeg', 'webp', 'pdf'] +allFormats = ["svg", "jpg", "jpeg", "webp", "pdf"] # 'png' is tested by image-test allNames = [ - 'plot_types', - 'annotations', - 'shapes', - 'range_slider', - 'contour_legend-colorscale', - 'layout_image', - 'image_astronaut_source', - 'gl2d_no-clustering2', - 'gl3d_surface-heatmap-treemap_transparent-colorscale', - 'map_density-multiple_legend', - 'smith_modes', - 'zsmooth_methods', - 'fonts', - 'worldcup', - 'mathjax' + "annotations", + "contour_legend-colorscale", + "fonts", + "gl2d_no-clustering2", + "gl3d_surface-heatmap-treemap_transparent-colorscale", + "image_astronaut_source", + "layout_image", + "map_density-multiple_legend", + "mathjax", + "plot_types", + "range_slider", + "shapes", + "smith_modes", + "worldcup", + "zsmooth_methods", ] failed = 0 -for name in allNames : - for fmt in allFormats : - print(name + ' --> ' + fmt) - with open(os.path.join(dirIn, name + '.json'), 'r') as _in : - fig = json.load(_in) - width = 700 - height = 500 - if 'layout' in fig : - layout = fig['layout'] - if 'autosize' not in layout or layout['autosize'] != True : - if 'width' in layout : - width = layout['width'] - if 'height' in layout : - height = layout['height'] +async def make_exports_async(): + global failed + + async with kaleido.Kaleido(n=1, plotlyjs=plotlyjs) as k: + for name in allNames: + with open(os.path.join(dirIn, name + ".json"), "r") as _in: + fig = json.load(_in) + + width = 700 + height = 500 + if "layout" in fig: + layout = fig["layout"] + if "autosize" not in layout or layout["autosize"] != True: + if "width" in layout: + width = layout["width"] + if "height" in layout: + height = layout["height"] + + for fmt in allFormats: + print(name + " --> " + fmt) + + try: + data = await k.calc_fig( + fig, + opts=dict( + format=fmt, + width=width, + height=height, + ), + topojson=topojson, + ) + filename = os.path.join(dirOut, name + "." + fmt) + with open(filename, "wb") as f: + f.write(data) - try : - pio.write_image( - fig=fig, - file=os.path.join(dirOut, name + '.' + fmt), - width=width, - height=height, - validate=False - ) + except Exception as e: + print(e) + failed += 1 - except Exception as e : - print(e) - failed += 1 -if failed > 0 : sys.exit(1) +if __name__ == "__main__": + asyncio.run(make_exports_async()) + if failed > 0: + sys.exit(1) diff --git a/test/image/mocks/zz-geo_choropleth-egypt-sudan.json b/test/image/mocks/geo_choropleth-egypt-sudan.json similarity index 100% rename from test/image/mocks/zz-geo_choropleth-egypt-sudan.json rename to test/image/mocks/geo_choropleth-egypt-sudan.json diff --git a/test/image/mocks/geo_choropleth-usa-location-names.json b/test/image/mocks/geo_choropleth-usa-location-names.json new file mode 100644 index 00000000000..59495991023 --- /dev/null +++ b/test/image/mocks/geo_choropleth-usa-location-names.json @@ -0,0 +1,152 @@ +{ + "data": [ + { + "autocolorscale": false, + "zmax": 16472.88, + "colorscale": [ + [0, "rgb(242,240,247)"], + [0.2, "rgb(218,218,235)"], + [0.4, "rgb(188,189,220)"], + [0.6, "rgb(158,154,200)"], + [0.8, "rgb(117,107,177)"], + [1, "rgb(84,39,143)"] + ], + "text": [ + "Alabama
--
Beef: 34.4
Pork: 10.6
Poultry: 481.0
Dairy: 4.06
--
Fruits: 25.11
Veggies: 14.33
--
Wheat: 70.0
Corn: 34.9
Cotton: 317.61", + "Alaska
--
Beef: 0.2
Pork: 0.1
Poultry: 0.0
Dairy: 0.19
--
Fruits: 0.0
Veggies: 1.56
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Arizona
--
Beef: 71.3
Pork: 17.9
Poultry: 0.0
Dairy: 105.48
--
Fruits: 60.27
Veggies: 386.91
--
Wheat: 48.7
Corn: 7.3
Cotton: 423.95", + "Arkansas
--
Beef: 53.2
Pork: 29.4
Poultry: 562.9
Dairy: 3.53
--
Fruits: 6.88
Veggies: 11.45
--
Wheat: 114.5
Corn: 69.5
Cotton: 665.44", + " California
--
Beef: 228.7
Pork: 11.1
Poultry: 225.4
Dairy: 929.95
--
Fruits: 8736.4
Veggies: 2106.79
--
Wheat: 249.3
Corn: 34.6
Cotton: 1064.95", + "Colorado
--
Beef: 261.4
Pork: 66.0
Poultry: 14.0
Dairy: 71.94
--
Fruits: 17.99
Veggies: 118.27
--
Wheat: 400.5
Corn: 183.2
Cotton: 0.0", + "Connecticut
--
Beef: 1.1
Pork: 0.1
Poultry: 6.9
Dairy: 9.49
--
Fruits: 13.1
Veggies: 11.16
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Delaware
--
Beef: 0.4
Pork: 0.6
Poultry: 114.7
Dairy: 2.3
--
Fruits: 1.53
Veggies: 20.03
--
Wheat: 22.9
Corn: 26.9
Cotton: 0.0", + "Florida
--
Beef: 42.6
Pork: 0.9
Poultry: 56.9
Dairy: 66.31
--
Fruits: 1371.36
Veggies: 450.86
--
Wheat: 1.8
Corn: 3.5
Cotton: 78.24", + "Georgia
--
Beef: 31.0
Pork: 18.9
Poultry: 630.4
Dairy: 38.38
--
Fruits: 233.51
Veggies: 154.77
--
Wheat: 65.4
Corn: 57.8
Cotton: 1154.07", + "Hawaii
--
Beef: 4.0
Pork: 0.7
Poultry: 1.3
Dairy: 1.16
--
Fruits: 55.51
Veggies: 24.83
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Idaho
--
Beef: 119.8
Pork: 0.0
Poultry: 2.4
Dairy: 294.6
--
Fruits: 21.64
Veggies: 319.19
--
Wheat: 568.2
Corn: 24.0
Cotton: 0.0", + "Illinois
--
Beef: 53.7
Pork: 394.0
Poultry: 14.0
Dairy: 45.82
--
Fruits: 12.53
Veggies: 39.95
--
Wheat: 223.8
Corn: 2228.5
Cotton: 0.0", + "Indiana
--
Beef: 21.9
Pork: 341.9
Poultry: 165.6
Dairy: 89.7
--
Fruits: 12.98
Veggies: 37.89
--
Wheat: 114.0
Corn: 1123.2
Cotton: 0.0", + "Iowa
--
Beef: 289.8
Pork: 1895.6
Poultry: 155.6
Dairy: 107.0
--
Fruits: 3.24
Veggies: 7.1
--
Wheat: 3.1
Corn: 2529.8
Cotton: 0.0", + "Kansas
--
Beef: 659.3
Pork: 179.4
Poultry: 6.4
Dairy: 65.45
--
Fruits: 3.11
Veggies: 9.32
--
Wheat: 1426.5
Corn: 457.3
Cotton: 43.98", + "Kentucky
--
Beef: 54.8
Pork: 34.2
Poultry: 151.3
Dairy: 28.27
--
Fruits: 6.6
Veggies: 0.0
--
Wheat: 149.3
Corn: 179.1
Cotton: 0.0", + "Louisiana
--
Beef: 19.8
Pork: 0.8
Poultry: 77.2
Dairy: 6.02
--
Fruits: 17.83
Veggies: 17.25
--
Wheat: 78.7
Corn: 91.4
Cotton: 280.42", + "Maine
--
Beef: 1.4
Pork: 0.5
Poultry: 10.4
Dairy: 16.18
--
Fruits: 52.01
Veggies: 62.9
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Maryland
--
Beef: 5.6
Pork: 3.1
Poultry: 127.0
Dairy: 24.81
--
Fruits: 12.9
Veggies: 20.43
--
Wheat: 55.8
Corn: 54.1
Cotton: 0.0", + "Massachusetts
--
Beef: 0.6
Pork: 0.5
Poultry: 0.6
Dairy: 5.81
--
Fruits: 80.83
Veggies: 21.13
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Michigan
--
Beef: 37.7
Pork: 118.1
Poultry: 32.6
Dairy: 214.82
--
Fruits: 257.69
Veggies: 189.96
--
Wheat: 247.0
Corn: 381.5
Cotton: 0.0", + "Minnesota
--
Beef: 112.3
Pork: 740.4
Poultry: 189.2
Dairy: 218.05
--
Fruits: 7.91
Veggies: 120.37
--
Wheat: 538.1
Corn: 1264.3
Cotton: 0.0", + "Mississippi
--
Beef: 12.8
Pork: 30.4
Poultry: 370.8
Dairy: 5.45
--
Fruits: 17.04
Veggies: 27.87
--
Wheat: 102.2
Corn: 110.0
Cotton: 494.75", + "Missouri
--
Beef: 137.2
Pork: 277.3
Poultry: 196.1
Dairy: 34.26
--
Fruits: 13.18
Veggies: 17.9
--
Wheat: 161.7
Corn: 428.8
Cotton: 345.29", + "Montana
--
Beef: 105.0
Pork: 16.7
Poultry: 1.7
Dairy: 6.82
--
Fruits: 3.3
Veggies: 45.27
--
Wheat: 1198.1
Corn: 5.4
Cotton: 0.0", + "Nebraska
--
Beef: 762.2
Pork: 262.5
Poultry: 31.4
Dairy: 30.07
--
Fruits: 2.16
Veggies: 53.5
--
Wheat: 292.3
Corn: 1735.9
Cotton: 0.0", + "Nevada
--
Beef: 21.8
Pork: 0.2
Poultry: 0.0
Dairy: 16.57
--
Fruits: 1.19
Veggies: 27.93
--
Wheat: 5.4
Corn: 0.0
Cotton: 0.0", + "New Hampshire
--
Beef: 0.6
Pork: 0.2
Poultry: 0.8
Dairy: 7.46
--
Fruits: 7.98
Veggies: 4.5
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "New Jersey
--
Beef: 0.8
Pork: 0.4
Poultry: 4.6
Dairy: 3.37
--
Fruits: 109.45
Veggies: 56.54
--
Wheat: 6.7
Corn: 10.1
Cotton: 0.0", + "New Mexico
--
Beef: 117.2
Pork: 0.1
Poultry: 0.3
Dairy: 191.01
--
Fruits: 101.9
Veggies: 43.88
--
Wheat: 13.9
Corn: 11.2
Cotton: 72.62", + "New York
--
Beef: 22.2
Pork: 5.8
Poultry: 17.7
Dairy: 331.8
--
Fruits: 202.56
Veggies: 143.37
--
Wheat: 29.9
Corn: 106.1
Cotton: 0.0", + "North Carolina
--
Beef: 24.8
Pork: 702.8
Poultry: 598.4
Dairy: 24.9
--
Fruits: 74.47
Veggies: 150.45
--
Wheat: 200.3
Corn: 92.2
Cotton: 470.86", + "North Dakota
--
Beef: 78.5
Pork: 16.1
Poultry: 0.5
Dairy: 8.14
--
Fruits: 0.25
Veggies: 130.79
--
Wheat: 1664.5
Corn: 236.1
Cotton: 0.0", + "Ohio
--
Beef: 36.2
Pork: 199.1
Poultry: 129.9
Dairy: 134.57
--
Fruits: 27.21
Veggies: 53.53
--
Wheat: 207.4
Corn: 535.1
Cotton: 0.0", + "Oklahoma
--
Beef: 337.6
Pork: 265.3
Poultry: 131.1
Dairy: 24.35
--
Fruits: 9.24
Veggies: 8.9
--
Wheat: 324.8
Corn: 27.5
Cotton: 110.54", + "Oregon
--
Beef: 58.8
Pork: 1.4
Poultry: 14.2
Dairy: 63.66
--
Fruits: 315.04
Veggies: 126.5
--
Wheat: 320.3
Corn: 11.7
Cotton: 0.0", + "Pennsylvania
--
Beef: 50.9
Pork: 91.3
Poultry: 169.8
Dairy: 280.87
--
Fruits: 89.48
Veggies: 38.26
--
Wheat: 41.0
Corn: 112.1
Cotton: 0.0", + "Rhode Island
--
Beef: 0.1
Pork: 0.1
Poultry: 0.2
Dairy: 0.52
--
Fruits: 2.83
Veggies: 3.02
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "South Carolina
--
Beef: 15.2
Pork: 10.9
Poultry: 186.5
Dairy: 7.62
--
Fruits: 53.45
Veggies: 42.66
--
Wheat: 55.3
Corn: 32.1
Cotton: 206.1", + "South Dakota
--
Beef: 193.5
Pork: 160.2
Poultry: 29.3
Dairy: 46.77
--
Fruits: 0.8
Veggies: 4.06
--
Wheat: 704.5
Corn: 643.6
Cotton: 0.0", + "Tennessee
--
Beef: 51.1
Pork: 17.6
Poultry: 82.4
Dairy: 21.18
--
Fruits: 6.23
Veggies: 24.67
--
Wheat: 100.0
Corn: 88.8
Cotton: 363.83", + "Texas
--
Beef: 961.0
Pork: 42.7
Poultry: 339.2
Dairy: 240.55
--
Fruits: 99.9
Veggies: 115.23
--
Wheat: 309.7
Corn: 167.2
Cotton: 2308.76", + "Utah
--
Beef: 27.9
Pork: 59.0
Poultry: 23.1
Dairy: 48.6
--
Fruits: 12.34
Veggies: 6.6
--
Wheat: 42.8
Corn: 5.3
Cotton: 0.0", + "Vermont
--
Beef: 6.2
Pork: 0.2
Poultry: 0.9
Dairy: 65.98
--
Fruits: 8.01
Veggies: 4.05
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Virginia
--
Beef: 39.5
Pork: 16.9
Poultry: 164.7
Dairy: 47.85
--
Fruits: 36.48
Veggies: 27.25
--
Wheat: 77.5
Corn: 39.5
Cotton: 64.84", + "Washington
--
Beef: 59.2
Pork: 0.0
Poultry: 35.6
Dairy: 154.18
--
Fruits: 1738.57
Veggies: 363.79
--
Wheat: 786.3
Corn: 29.5
Cotton: 0.0", + "West Virginia
--
Beef: 12.0
Pork: 0.3
Poultry: 45.4
Dairy: 3.9
--
Fruits: 11.54
Veggies: 0.0
--
Wheat: 1.6
Corn: 3.5
Cotton: 0.0", + "Wisconsin
--
Beef: 107.3
Pork: 38.6
Poultry: 34.5
Dairy: 633.6
--
Fruits: 133.8
Veggies: 148.99
--
Wheat: 96.7
Corn: 460.5
Cotton: 0.0", + "Wyoming
--
Beef: 75.1
Pork: 33.2
Poultry: 0.1
Dairy: 2.89
--
Fruits: 0.17
Veggies: 10.23
--
Wheat: 20.7
Corn: 9.0
Cotton: 0.0" + ], + "zmin": 13.31, + "locations": [ + "AL", + "AK", + "AZ", + "AR", + "CA", + "CO", + "CT", + "DE", + "FL", + "GA", + "HI", + "ID", + "IL", + "IN", + "IA", + "KS", + "KY", + "LA", + "ME", + "MD", + "MA", + "MI", + "MN", + "MS", + "MO", + "MT", + "NE", + "NV", + "NH", + "NJ", + "NM", + "NY", + "NC", + "ND", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VT", + "VA", + "WA", + "WV", + "WI", + "WY" + ], + "colorbar": { + "title": { "text": "Millions USD" } + }, + "type": "choropleth", + "z": [ + 1390.63, 13.31, 1463.17, 3586.02, 16472.88, 1851.33, 259.62, 282.19, + 3764.09, 2860.84, 401.84, 2078.89, 8709.48, 5050.23, 11273.76, 4589.01, + 1889.15, 1914.23, 278.37, 692.75, 248.65, 3164.16, 7192.33, 2170.8, + 3933.42, 1718, 7114.13, 139.89, 73.06, 500.4, 751.58, 1488.9, 3806.05, + 3761.96, 3979.79, 1646.41, 1794.57, 1969.87, 31.59, 929.93, 3770.19, + 1535.13, 6648.22, 453.39, 180.14, 1146.48, 3894.81, 138.89, 3090.23, + 349.69 + ], + "locationmode": "USA-states" + } + ], + "layout": { + "autosize": true, + "title": { + "text": "2011 US Agriculture Exports by State - Location Names
(Hover for breakdown)" + }, + "showlegend": false, + "height": 598, + "width": 870, + "geo": { + "showlakes": true, + "scope": "usa", + "projection": { + "type": "albers usa" + }, + "lakecolor": "rgb(255, 255, 255)" + } + } +} diff --git a/test/image/mocks/geo_choropleth-usa.json b/test/image/mocks/geo_choropleth-usa.json index 88329cbb828..b3a2255a730 100644 --- a/test/image/mocks/geo_choropleth-usa.json +++ b/test/image/mocks/geo_choropleth-usa.json @@ -2,13 +2,6 @@ "data": [ { "autocolorscale": false, - "colorbar": { - "title": { "text": "Millions USD" } - }, - "locationmode": "USA-states", - "name": "", - "type": "choropleth", - "zmin": 13.31, "zmax": 16472.88, "colorscale": [ [0, "rgb(242,240,247)"], @@ -70,6 +63,7 @@ "Wisconsin
--
Beef: 107.3
Pork: 38.6
Poultry: 34.5
Dairy: 633.6
--
Fruits: 133.8
Veggies: 148.99
--
Wheat: 96.7
Corn: 460.5
Cotton: 0.0", "Wyoming
--
Beef: 75.1
Pork: 33.2
Poultry: 0.1
Dairy: 2.89
--
Fruits: 0.17
Veggies: 10.23
--
Wheat: 20.7
Corn: 9.0
Cotton: 0.0" ], + "zmin": 13.31, "locations": [ "AL", "AK", @@ -122,137 +116,10 @@ "WI", "WY" ], - "z": [ - 1390.63, 13.31, 1463.17, 3586.02, 16472.88, 1851.33, 259.62, 282.19, - 3764.09, 2860.84, 401.84, 2078.89, 8709.48, 5050.23, 11273.76, 4589.01, - 1889.15, 1914.23, 278.37, 692.75, 248.65, 3164.16, 7192.33, 2170.8, - 3933.42, 1718, 7114.13, 139.89, 73.06, 500.4, 751.58, 1488.9, 3806.05, - 3761.96, 3979.79, 1646.41, 1794.57, 1969.87, 31.59, 929.93, 3770.19, - 1535.13, 6648.22, 453.39, 180.14, 1146.48, 3894.81, 138.89, 3090.23, - 349.69 - ] - }, - { - "autocolorscale": false, - "geo": "geo2", - "locationmode": "USA-states", - "name": "", - "showscale": false, + "colorbar": { + "title": { "text": "Millions USD" } + }, "type": "choropleth", - "zmax": 16472.88, - "zmin": 13.31, - "colorscale": [ - [0, "rgb(242,240,247)"], - [0.2, "rgb(218,218,235)"], - [0.4, "rgb(188,189,220)"], - [0.6, "rgb(158,154,200)"], - [0.8, "rgb(117,107,177)"], - [1, "rgb(84,39,143)"] - ], - "text": [ - "Alabama
--
Beef: 34.4
Pork: 10.6
Poultry: 481.0
Dairy: 4.06
--
Fruits: 25.11
Veggies: 14.33
--
Wheat: 70.0
Corn: 34.9
Cotton: 317.61", - "Alaska
--
Beef: 0.2
Pork: 0.1
Poultry: 0.0
Dairy: 0.19
--
Fruits: 0.0
Veggies: 1.56
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", - "Arizona
--
Beef: 71.3
Pork: 17.9
Poultry: 0.0
Dairy: 105.48
--
Fruits: 60.27
Veggies: 386.91
--
Wheat: 48.7
Corn: 7.3
Cotton: 423.95", - "Arkansas
--
Beef: 53.2
Pork: 29.4
Poultry: 562.9
Dairy: 3.53
--
Fruits: 6.88
Veggies: 11.45
--
Wheat: 114.5
Corn: 69.5
Cotton: 665.44", - " California
--
Beef: 228.7
Pork: 11.1
Poultry: 225.4
Dairy: 929.95
--
Fruits: 8736.4
Veggies: 2106.79
--
Wheat: 249.3
Corn: 34.6
Cotton: 1064.95", - "Colorado
--
Beef: 261.4
Pork: 66.0
Poultry: 14.0
Dairy: 71.94
--
Fruits: 17.99
Veggies: 118.27
--
Wheat: 400.5
Corn: 183.2
Cotton: 0.0", - "Connecticut
--
Beef: 1.1
Pork: 0.1
Poultry: 6.9
Dairy: 9.49
--
Fruits: 13.1
Veggies: 11.16
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", - "Delaware
--
Beef: 0.4
Pork: 0.6
Poultry: 114.7
Dairy: 2.3
--
Fruits: 1.53
Veggies: 20.03
--
Wheat: 22.9
Corn: 26.9
Cotton: 0.0", - "Florida
--
Beef: 42.6
Pork: 0.9
Poultry: 56.9
Dairy: 66.31
--
Fruits: 1371.36
Veggies: 450.86
--
Wheat: 1.8
Corn: 3.5
Cotton: 78.24", - "Georgia
--
Beef: 31.0
Pork: 18.9
Poultry: 630.4
Dairy: 38.38
--
Fruits: 233.51
Veggies: 154.77
--
Wheat: 65.4
Corn: 57.8
Cotton: 1154.07", - "Hawaii
--
Beef: 4.0
Pork: 0.7
Poultry: 1.3
Dairy: 1.16
--
Fruits: 55.51
Veggies: 24.83
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", - "Idaho
--
Beef: 119.8
Pork: 0.0
Poultry: 2.4
Dairy: 294.6
--
Fruits: 21.64
Veggies: 319.19
--
Wheat: 568.2
Corn: 24.0
Cotton: 0.0", - "Illinois
--
Beef: 53.7
Pork: 394.0
Poultry: 14.0
Dairy: 45.82
--
Fruits: 12.53
Veggies: 39.95
--
Wheat: 223.8
Corn: 2228.5
Cotton: 0.0", - "Indiana
--
Beef: 21.9
Pork: 341.9
Poultry: 165.6
Dairy: 89.7
--
Fruits: 12.98
Veggies: 37.89
--
Wheat: 114.0
Corn: 1123.2
Cotton: 0.0", - "Iowa
--
Beef: 289.8
Pork: 1895.6
Poultry: 155.6
Dairy: 107.0
--
Fruits: 3.24
Veggies: 7.1
--
Wheat: 3.1
Corn: 2529.8
Cotton: 0.0", - "Kansas
--
Beef: 659.3
Pork: 179.4
Poultry: 6.4
Dairy: 65.45
--
Fruits: 3.11
Veggies: 9.32
--
Wheat: 1426.5
Corn: 457.3
Cotton: 43.98", - "Kentucky
--
Beef: 54.8
Pork: 34.2
Poultry: 151.3
Dairy: 28.27
--
Fruits: 6.6
Veggies: 0.0
--
Wheat: 149.3
Corn: 179.1
Cotton: 0.0", - "Louisiana
--
Beef: 19.8
Pork: 0.8
Poultry: 77.2
Dairy: 6.02
--
Fruits: 17.83
Veggies: 17.25
--
Wheat: 78.7
Corn: 91.4
Cotton: 280.42", - "Maine
--
Beef: 1.4
Pork: 0.5
Poultry: 10.4
Dairy: 16.18
--
Fruits: 52.01
Veggies: 62.9
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", - "Maryland
--
Beef: 5.6
Pork: 3.1
Poultry: 127.0
Dairy: 24.81
--
Fruits: 12.9
Veggies: 20.43
--
Wheat: 55.8
Corn: 54.1
Cotton: 0.0", - "Massachusetts
--
Beef: 0.6
Pork: 0.5
Poultry: 0.6
Dairy: 5.81
--
Fruits: 80.83
Veggies: 21.13
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", - "Michigan
--
Beef: 37.7
Pork: 118.1
Poultry: 32.6
Dairy: 214.82
--
Fruits: 257.69
Veggies: 189.96
--
Wheat: 247.0
Corn: 381.5
Cotton: 0.0", - "Minnesota
--
Beef: 112.3
Pork: 740.4
Poultry: 189.2
Dairy: 218.05
--
Fruits: 7.91
Veggies: 120.37
--
Wheat: 538.1
Corn: 1264.3
Cotton: 0.0", - "Mississippi
--
Beef: 12.8
Pork: 30.4
Poultry: 370.8
Dairy: 5.45
--
Fruits: 17.04
Veggies: 27.87
--
Wheat: 102.2
Corn: 110.0
Cotton: 494.75", - "Missouri
--
Beef: 137.2
Pork: 277.3
Poultry: 196.1
Dairy: 34.26
--
Fruits: 13.18
Veggies: 17.9
--
Wheat: 161.7
Corn: 428.8
Cotton: 345.29", - "Montana
--
Beef: 105.0
Pork: 16.7
Poultry: 1.7
Dairy: 6.82
--
Fruits: 3.3
Veggies: 45.27
--
Wheat: 1198.1
Corn: 5.4
Cotton: 0.0", - "Nebraska
--
Beef: 762.2
Pork: 262.5
Poultry: 31.4
Dairy: 30.07
--
Fruits: 2.16
Veggies: 53.5
--
Wheat: 292.3
Corn: 1735.9
Cotton: 0.0", - "Nevada
--
Beef: 21.8
Pork: 0.2
Poultry: 0.0
Dairy: 16.57
--
Fruits: 1.19
Veggies: 27.93
--
Wheat: 5.4
Corn: 0.0
Cotton: 0.0", - "New Hampshire
--
Beef: 0.6
Pork: 0.2
Poultry: 0.8
Dairy: 7.46
--
Fruits: 7.98
Veggies: 4.5
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", - "New Jersey
--
Beef: 0.8
Pork: 0.4
Poultry: 4.6
Dairy: 3.37
--
Fruits: 109.45
Veggies: 56.54
--
Wheat: 6.7
Corn: 10.1
Cotton: 0.0", - "New Mexico
--
Beef: 117.2
Pork: 0.1
Poultry: 0.3
Dairy: 191.01
--
Fruits: 101.9
Veggies: 43.88
--
Wheat: 13.9
Corn: 11.2
Cotton: 72.62", - "New York
--
Beef: 22.2
Pork: 5.8
Poultry: 17.7
Dairy: 331.8
--
Fruits: 202.56
Veggies: 143.37
--
Wheat: 29.9
Corn: 106.1
Cotton: 0.0", - "North Carolina
--
Beef: 24.8
Pork: 702.8
Poultry: 598.4
Dairy: 24.9
--
Fruits: 74.47
Veggies: 150.45
--
Wheat: 200.3
Corn: 92.2
Cotton: 470.86", - "North Dakota
--
Beef: 78.5
Pork: 16.1
Poultry: 0.5
Dairy: 8.14
--
Fruits: 0.25
Veggies: 130.79
--
Wheat: 1664.5
Corn: 236.1
Cotton: 0.0", - "Ohio
--
Beef: 36.2
Pork: 199.1
Poultry: 129.9
Dairy: 134.57
--
Fruits: 27.21
Veggies: 53.53
--
Wheat: 207.4
Corn: 535.1
Cotton: 0.0", - "Oklahoma
--
Beef: 337.6
Pork: 265.3
Poultry: 131.1
Dairy: 24.35
--
Fruits: 9.24
Veggies: 8.9
--
Wheat: 324.8
Corn: 27.5
Cotton: 110.54", - "Oregon
--
Beef: 58.8
Pork: 1.4
Poultry: 14.2
Dairy: 63.66
--
Fruits: 315.04
Veggies: 126.5
--
Wheat: 320.3
Corn: 11.7
Cotton: 0.0", - "Pennsylvania
--
Beef: 50.9
Pork: 91.3
Poultry: 169.8
Dairy: 280.87
--
Fruits: 89.48
Veggies: 38.26
--
Wheat: 41.0
Corn: 112.1
Cotton: 0.0", - "Rhode Island
--
Beef: 0.1
Pork: 0.1
Poultry: 0.2
Dairy: 0.52
--
Fruits: 2.83
Veggies: 3.02
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", - "South Carolina
--
Beef: 15.2
Pork: 10.9
Poultry: 186.5
Dairy: 7.62
--
Fruits: 53.45
Veggies: 42.66
--
Wheat: 55.3
Corn: 32.1
Cotton: 206.1", - "South Dakota
--
Beef: 193.5
Pork: 160.2
Poultry: 29.3
Dairy: 46.77
--
Fruits: 0.8
Veggies: 4.06
--
Wheat: 704.5
Corn: 643.6
Cotton: 0.0", - "Tennessee
--
Beef: 51.1
Pork: 17.6
Poultry: 82.4
Dairy: 21.18
--
Fruits: 6.23
Veggies: 24.67
--
Wheat: 100.0
Corn: 88.8
Cotton: 363.83", - "Texas
--
Beef: 961.0
Pork: 42.7
Poultry: 339.2
Dairy: 240.55
--
Fruits: 99.9
Veggies: 115.23
--
Wheat: 309.7
Corn: 167.2
Cotton: 2308.76", - "Utah
--
Beef: 27.9
Pork: 59.0
Poultry: 23.1
Dairy: 48.6
--
Fruits: 12.34
Veggies: 6.6
--
Wheat: 42.8
Corn: 5.3
Cotton: 0.0", - "Vermont
--
Beef: 6.2
Pork: 0.2
Poultry: 0.9
Dairy: 65.98
--
Fruits: 8.01
Veggies: 4.05
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", - "Virginia
--
Beef: 39.5
Pork: 16.9
Poultry: 164.7
Dairy: 47.85
--
Fruits: 36.48
Veggies: 27.25
--
Wheat: 77.5
Corn: 39.5
Cotton: 64.84", - "Washington
--
Beef: 59.2
Pork: 0.0
Poultry: 35.6
Dairy: 154.18
--
Fruits: 1738.57
Veggies: 363.79
--
Wheat: 786.3
Corn: 29.5
Cotton: 0.0", - "West Virginia
--
Beef: 12.0
Pork: 0.3
Poultry: 45.4
Dairy: 3.9
--
Fruits: 11.54
Veggies: 0.0
--
Wheat: 1.6
Corn: 3.5
Cotton: 0.0", - "Wisconsin
--
Beef: 107.3
Pork: 38.6
Poultry: 34.5
Dairy: 633.6
--
Fruits: 133.8
Veggies: 148.99
--
Wheat: 96.7
Corn: 460.5
Cotton: 0.0", - "Wyoming
--
Beef: 75.1
Pork: 33.2
Poultry: 0.1
Dairy: 2.89
--
Fruits: 0.17
Veggies: 10.23
--
Wheat: 20.7
Corn: 9.0
Cotton: 0.0" - ], - "locations": [ - "Alabama", - "Alaska", - "Arizona", - "Arkansas", - "California", - "Colorado", - "Connecticut", - "Delaware", - "Florida", - "Georgia", - "Hawaii", - "Idaho", - "Illinois", - "Indiana", - "Iowa", - "Kansas", - "Kentucky", - "Louisiana", - "Maine", - "Maryland", - "Massachusetts", - "Michigan", - "Minnesota", - "Mississippi", - "Missouri", - "Montana", - "Nebraska", - "Nevada", - "New Hampshire", - "New Jersey", - "New Mexico", - "New York", - "North Carolina", - "North Dakota", - "Ohio", - "Oklahoma", - "Oregon", - "Pennsylvania", - "Rhode Island", - "South Carolina", - "South Dakota", - "Tennessee", - "Texas", - "Utah", - "Vermont", - "Virginia", - "Washington", - "West Virginia", - "Wisconsin", - "Wyoming" - ], "z": [ 1390.63, 13.31, 1463.17, 3586.02, 16472.88, 1851.33, 259.62, 282.19, 3764.09, 2860.84, 401.84, 2078.89, 8709.48, 5050.23, 11273.76, 4589.01, @@ -261,16 +128,17 @@ 3761.96, 3979.79, 1646.41, 1794.57, 1969.87, 31.59, 929.93, 3770.19, 1535.13, 6648.22, 453.39, 180.14, 1146.48, 3894.81, 138.89, 3090.23, 349.69 - ] + ], + "locationmode": "USA-states" } ], "layout": { "autosize": true, "title": { - "text": "2011 US Agriculture Exports by State
(Hover for breakdown)" + "text": "2011 US Agriculture Exports by State - Two Letter Abbreviations
(Hover for breakdown)" }, "showlegend": false, - "height": 1200, + "height": 598, "width": 870, "geo": { "showlakes": true, @@ -278,37 +146,7 @@ "projection": { "type": "albers usa" }, - "lakecolor": "rgb(255, 255, 255)", - "domain": { "y": [0.55, 1] } - }, - "geo2": { - "showlakes": true, - "scope": "usa", - "projection": { - "type": "albers usa" - }, - "lakecolor": "rgb(255, 255, 255)", - "domain": { "y": [0, 0.45] } - }, - "annotations": [ - { - "text": "Lookup by two letter location abbreviation", - "x": 0.5, - "y": 0.53, - "xref": "paper", - "yref": "paper", - "showarrow": false, - "font": { "size": 16 } - }, - { - "text": "Lookup by full location name", - "x": 0.5, - "y": -0.02, - "xref": "paper", - "yref": "paper", - "showarrow": false, - "font": { "size": 16 } - } - ] + "lakecolor": "rgb(255, 255, 255)" + } } } diff --git a/test/image/mocks/zz-label-spacing.json b/test/image/mocks/label-spacing.json similarity index 100% rename from test/image/mocks/zz-label-spacing.json rename to test/image/mocks/label-spacing.json diff --git a/test/image/mocks/zz-legend-vertical-maxheight.json b/test/image/mocks/legend-vertical-maxheight.json similarity index 100% rename from test/image/mocks/zz-legend-vertical-maxheight.json rename to test/image/mocks/legend-vertical-maxheight.json diff --git a/test/image/mocks/zz-minorloglabels.json b/test/image/mocks/minorloglabels.json similarity index 100% rename from test/image/mocks/zz-minorloglabels.json rename to test/image/mocks/minorloglabels.json diff --git a/test/image/mocks/zz-pattern_bars-path.json b/test/image/mocks/pattern_bars-path.json similarity index 100% rename from test/image/mocks/zz-pattern_bars-path.json rename to test/image/mocks/pattern_bars-path.json diff --git a/test/image/mocks/zz-pie-slice-legend.json b/test/image/mocks/pie-slice-legend.json similarity index 100% rename from test/image/mocks/zz-pie-slice-legend.json rename to test/image/mocks/pie-slice-legend.json diff --git a/test/image/mocks/zz-pie-slice-legend2.json b/test/image/mocks/pie-slice-legend2.json similarity index 100% rename from test/image/mocks/zz-pie-slice-legend2.json rename to test/image/mocks/pie-slice-legend2.json diff --git a/test/image/mocks/zz-pie-slice-legend3.json b/test/image/mocks/pie-slice-legend3.json similarity index 100% rename from test/image/mocks/zz-pie-slice-legend3.json rename to test/image/mocks/pie-slice-legend3.json diff --git a/test/image/mocks/zz-scatter_marker_line_dash.json b/test/image/mocks/scatter_marker_line_dash.json similarity index 100% rename from test/image/mocks/zz-scatter_marker_line_dash.json rename to test/image/mocks/scatter_marker_line_dash.json diff --git a/test/image/mocks/zz_shapes_clipping_double_digit_subplots.json b/test/image/mocks/shapes_clipping_double_digit_subplots.json similarity index 100% rename from test/image/mocks/zz_shapes_clipping_double_digit_subplots.json rename to test/image/mocks/shapes_clipping_double_digit_subplots.json diff --git a/test/image/mocks/zz-tickson_boundaries_ticklabelposition.json b/test/image/mocks/tickson_boundaries_ticklabelposition.json similarity index 100% rename from test/image/mocks/zz-tickson_boundaries_ticklabelposition.json rename to test/image/mocks/tickson_boundaries_ticklabelposition.json diff --git a/test/image/mocks/zzz_zerolinelayer_above.json b/test/image/mocks/zerolinelayer_above.json similarity index 100% rename from test/image/mocks/zzz_zerolinelayer_above.json rename to test/image/mocks/zerolinelayer_above.json diff --git a/test/image/mocks/zzz_zerolinelayer_below.json b/test/image/mocks/zerolinelayer_below.json similarity index 100% rename from test/image/mocks/zzz_zerolinelayer_below.json rename to test/image/mocks/zerolinelayer_below.json diff --git a/test/jasmine/karma.conf.js b/test/jasmine/karma.conf.js index 2c7f075c3ee..fb412599a9b 100644 --- a/test/jasmine/karma.conf.js +++ b/test/jasmine/karma.conf.js @@ -38,7 +38,7 @@ var argv = minimist(process.argv.slice(4), { failFast: false, doNotFailOnEmptyTestSuite: false, width: '1035', - height: '617', + height: '800', verbose: false, showSkipped: isCI } @@ -261,12 +261,6 @@ func.defaultConfig = { '--touch-events', '--window-size=' + argv.width + ',' + argv.height, isCI ? '--ignore-gpu-blocklist' : '', - // The following two flags are needed only for the "NoCI" tests which run in GitHub Actions. - // The first is needed because the GPU is not available to those runners, - // and therefore we need to use SwiftShader instead (which is disabled by default as of Jan 2026, - // hence the need for a flag to manually enable it). - // The second flag is needed because the Chrome browser installed by the CI job runner - // fails without it. isCI && process.env.GITHUB_ACTIONS ? '--enable-unsafe-swiftshader' : '', isCI && process.env.GITHUB_ACTIONS ? '--no-sandbox' : '', isBundleTest && basename(testFileGlob) === 'no_webgl' ? '--disable-webgl' : '' diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 4d85a14fc37..6b336cb0c5c 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -1722,13 +1722,13 @@ describe('Test axes', function() { width: 600, height: 600 }).then(function() { - expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.110, 2]); + expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.12, 2]); return Plotly.relayout(gd, { 'xaxis.insiderange': [1, 3] }); }).then(function() { - expect(gd._fullLayout.xaxis.range).toBeCloseToArray([0.889, 3]); + expect(gd._fullLayout.xaxis.range).toBeCloseToArray([0.879, 3]); }).then(done, done.fail); }); }); @@ -8152,11 +8152,11 @@ describe('more react tests', function() { Plotly.newPlot(gd, fig1) .then(function() { - expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.110, 2]); + expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.12, 2]); return Plotly.react(gd, fig2); }).then(function() { - expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.164, 2]); + expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.173, 2]); }).then(done, done.fail); }); }); diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index 83d0f92a4c2..6faddf3c630 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -1404,7 +1404,7 @@ describe('Test geo interactions', function() { py -= 2; mouseEvent('mousemove', px, py); - if(py > 175) { + if(py > 176) { _assert('- py ' + py, 1); expect(cnt).toBe(0, 'no plotly_unhover event so far'); } else { @@ -2608,7 +2608,7 @@ describe('Test geo zoom/pan/drag interactions:', function() { _assert('base', [ [-96.6, 38.7], 1, ], [ - [410, 329], 738.5 + [410, 309], 738.5 ], undefined); return drag({path: [[250, 250], [200, 200]], noCover: true}); }) @@ -2616,7 +2616,7 @@ describe('Test geo zoom/pan/drag interactions:', function() { _assert('after NW-SE drag', [ [-91.8, 34.8], 1, ], [ - [366, 279], 738.5 + [366, 259], 738.5 ], [ 'geo.center.lon', 'geo.center.lon' ]); @@ -2626,7 +2626,7 @@ describe('Test geo zoom/pan/drag interactions:', function() { _assert('after scroll', [ [-94.5, 35.0], 1.3 ], [ - [380, 273], 974.4 + [380, 245.9], 974.4 ], [ 'geo.center.lon', 'geo.center.lon', 'geo.projection.scale' ]); @@ -2637,7 +2637,7 @@ describe('Test geo zoom/pan/drag interactions:', function() { [-94.5, 35.0], 1.3 ], [ // new center values are reflected in translate() - [380, 273], 974.4 + [380, 245.9], 974.4 ], [ 'geo.showlakes' ]); @@ -2647,7 +2647,7 @@ describe('Test geo zoom/pan/drag interactions:', function() { _assert('after double click', [ [-96.6, 38.7], 1, ], [ - [416, 329], 738.5 + [416, 309], 738.5 ], 'dblclick'); }) .then(done, done.fail); @@ -2759,10 +2759,10 @@ describe('Test geo interactions update marker angles:', function() { }) .then(function() { newPath = getPath(); - expect(newPath).toEqual('M0,0L18.224184922503092,8.238876374264322L19.586365339190138,-4.046516131164082Z'); + expect(newPath).toEqual('M0,0L18.27769005891461,8.119485581627321L19.559475756661865,-4.174554841483899Z'); expect(newPath).not.toEqual(initialPath); - expect(newPath).toEqual('M0,0L18.224184922503092,8.238876374264322L19.586365339190138,-4.046516131164082Z'); + expect(newPath).toEqual('M0,0L18.27769005891461,8.119485581627321L19.559475756661865,-4.174554841483899Z'); expect(initialPath).toEqual('M0,0L-1.5094067529528923,19.942960945008643L10.501042615957648,17.021401351764233Z'); }) .then(done, done.fail); diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index 76fa0e9f27c..69120330795 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -1144,10 +1144,17 @@ describe('sankey tests', function() { return function(elType) { return new Promise(function(resolve, reject) { - gd.once(eventType, function(d) { + const handler = (d) => { Lib.clearThrottle(); + const isNode = d.points[0].hasOwnProperty('sourceLinks'); + const isExpectedType = (elType === 'node') ? isNode : !isNode; + if (!isExpectedType) { + gd.once(eventType, handler); + return; + } resolve(d); - }); + } + gd.once(eventType, handler); mouseFn(posByElementType[elType]); setTimeout(function() { @@ -1460,8 +1467,8 @@ describe('sankey tests', function() { nodes = document.getElementsByClassName('sankey-node'); node = Array.prototype.slice.call(nodes).find(function(n) { return n.textContent === '0';}); var newPosition = getNodeCoords(node); - expect(newPosition.x).toBeCloseTo(positionAfterDrag[0], 2, 'final x position is off'); - expect(newPosition.y).toBeCloseTo(positionAfterDrag[1], 2, 'final y position is off'); + expect(newPosition.x).toBeCloseTo(positionAfterDrag[0], -1, 'final x position is off'); + expect(newPosition.y).toBeCloseTo(positionAfterDrag[1], -1, 'final y position is off'); // Change color of nodes var mockCopy = Lib.extendDeep({}, mockCircularFreeform); @@ -1485,8 +1492,8 @@ describe('sankey tests', function() { pos = positionBeforeDrag; msg = 'should go back to its default because uirevision changed'; } - expect(newPosition.x).toBeCloseTo(pos[0], 2, 'x position ' + msg); - expect(newPosition.y).toBeCloseTo(pos[1], 2, 'y position ' + msg); + expect(newPosition.x).toBeCloseTo(pos[0], -1, 'x position ' + msg); + expect(newPosition.y).toBeCloseTo(pos[1], -1, 'y position ' + msg); }) .then(done, done.fail); }); diff --git a/test/jasmine/tests/scatter3d_test.js b/test/jasmine/tests/scatter3d_test.js index ea6916ae435..a5f1a7f8b4d 100644 --- a/test/jasmine/tests/scatter3d_test.js +++ b/test/jasmine/tests/scatter3d_test.js @@ -204,7 +204,6 @@ describe('Test scatter3d interactions:', function() { } Plotly.newPlot(gd, _mock) - .then(delay(20)) .then(function() { assertObjects(order0); diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 194c91cdc69..41a9a3d2211 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -1876,8 +1876,18 @@ describe('Test select box and lasso in general:', function() { }).then(function() { return Plotly.relayout(gd, 'dragmode', 'select'); }).then(function() { + var plots = gd._fullLayout._plots; + Object.keys(plots).forEach(function(id) { + var p = plots[id]; + var xa = p.xaxis, ya = p.yaxis; + if(xa && ya) { + console.log('[subplot ' + id + '] x: ' + xa._offset + '-' + (xa._offset + xa._length) + + ', y: ' + ya._offset + '-' + (ya._offset + ya._length)); + } + }); return drag([[150, 450], [650, 350]]); }).then(function() { + console.log('[after drag 1] selectedpoints:', gd.data.map(function(t) { return t.selectedpoints; })); expect(gd.data[0].selectedpoints).toBe(undefined); expect(gd.data[1].selectedpoints).toEqual([1, 2]); expect(gd.data[2].selectedpoints).toBe(undefined); @@ -1885,6 +1895,7 @@ describe('Test select box and lasso in general:', function() { }).then(function() { return drag([[150, 100], [600, 200]]); }).then(function() { + console.log('[after drag 2] selectedpoints:', gd.data.map(function(t) { return t.selectedpoints; })); expect(gd.data[0].selectedpoints).toEqual([1, 2]); expect(gd.data[1].selectedpoints).toEqual([1, 2]); expect(gd.data[2].selectedpoints).toEqual([1]); @@ -1892,6 +1903,7 @@ describe('Test select box and lasso in general:', function() { }).then(function() { return drag([[600, 150], [650, 150]]); // Extend existing selection }).then(function() { + console.log('[after drag 3] selectedpoints:', gd.data.map(function(t) { return t.selectedpoints; })); expect(gd.data[0].selectedpoints).toEqual([1, 2]); expect(gd.data[1].selectedpoints).toEqual([1, 2]); expect(gd.data[2].selectedpoints).toEqual([1, 2]); @@ -2299,7 +2311,7 @@ describe('Test select box and lasso per trace:', function() { [[150, 150], [300, 300]], function() { assertPoints([['NY', 10]]); - assertRanges([[-83.38, 46.13], [-74.06, 39.29]]); + assertRanges([[-83.35, 46.13], [-74.03, 39.29]]); assertSelectedPoints({0: [0]}); }, null, BOXEVENTS, 'choroplethmap select' @@ -2315,8 +2327,8 @@ describe('Test select box and lasso per trace:', function() { assertPoints([['MA', 20]]); assertSelectedPoints({0: [1]}); assertLassoPoints([ - [-74.06, 43.936], [-74.06, 39.293], [-67.84, 39.293], - [-67.84, 43.936], [-74.06, 43.936] + [-74.03, 43.936], [-74.03, 39.293], [-67.81, 39.293], + [-67.81, 43.936], [-74.03, 43.936] ]); }, null, LASSOEVENTS, 'choroplethmap lasso' @@ -2656,12 +2668,14 @@ describe('Test select box and lasso per trace:', function() { assertPoints([ [0, 281, 'Purchases'], [0, 269, 'Material expenses'], + [0, 191, 'Personnel expenses'], + [0, 179, 'Other expenses'], ]); assertSelectedPoints({ - 0: [5, 6] + 0: [5, 6, 7, 8] }); }, - null, [3, 2, 1], 'waterfall lasso' + null, [4, 2, 1], 'waterfall lasso' ); }) .then(function() { @@ -2705,13 +2719,14 @@ describe('Test select box and lasso per trace:', function() { assertPoints([ [0, 331.5, 'Author: etpinard'], [1, 53.5, 'Pull requests'], + [1, 15.5, 'Author: etpinard'], ]); assertSelectedPoints({ 0: [2], - 1: [1] + 1: [1, 2] }); }, - null, [3, 2, 1], 'funnel lasso' + null, [4, 2, 1], 'funnel lasso' ); }) .then(done, done.fail); @@ -3336,7 +3351,7 @@ describe('Test select box and lasso per trace:', function() { [[150, 150], [300, 300]], function() { assertPoints([['NY', 10]]); - assertRanges([[-83.38, 46.13], [-74.06, 39.29]]); + assertRanges([[-83.35, 46.13], [-74.03, 39.29]]); assertSelectedPoints({0: [0], 3: []}); }, null, BOXEVENTS, 'choroplethmap select' @@ -3352,8 +3367,8 @@ describe('Test select box and lasso per trace:', function() { assertPoints([['MA', 20], []]); assertSelectedPoints({0: [1], 3: [0]}); assertLassoPoints([ - [-74.06, 43.936], [-74.06, 39.293], [-67.84, 39.293], - [-67.84, 43.936], [-74.06, 43.936] + [-74.03, 43.936], [-74.03, 39.293], [-67.81, 39.293], + [-67.81, 43.936], [-74.03, 43.936] ]); }, null, LASSOEVENTS, 'choroplethmap lasso'