diff --git a/.github/workflows/appengine_deploy.yml b/.github/workflows/appengine_deploy.yml index 50afec2407c..efc6fe9417d 100644 --- a/.github/workflows/appengine_deploy.yml +++ b/.github/workflows/appengine_deploy.yml @@ -15,7 +15,7 @@ jobs: steps: # Checks-out the repository under $GITHUB_WORKSPACE. # When running manually this checks out the master branch. - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Prepare demo files # Install all dependencies, then copy all the files needed for demos. diff --git a/.github/workflows/browser_test.yml b/.github/workflows/browser_test.yml index 51ac0dffada..c2ce9913635 100644 --- a/.github/workflows/browser_test.yml +++ b/.github/workflows/browser_test.yml @@ -26,7 +26,7 @@ jobs: # https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: persist-credentials: false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c4ab688f8fd..cdec5308279 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,12 +18,12 @@ jobs: # TODO (#2114): re-enable osx build. # os: [ubuntu-latest, macos-latest] os: [ubuntu-latest] - node-version: [18.x, 20.x, 22.x] + node-version: [18.x, 20.x, 22.x, 24.x] # See supported Node.js release schedule at # https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: persist-credentials: false @@ -54,7 +54,7 @@ jobs: timeout-minutes: 5 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Use Node.js 20.x uses: actions/setup-node@v4 @@ -71,7 +71,7 @@ jobs: timeout-minutes: 5 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Use Node.js 20.x uses: actions/setup-node@v4 diff --git a/.github/workflows/keyboard_plugin_test.yml b/.github/workflows/keyboard_plugin_test.yml index 753d31dda1e..3d7d3d5d444 100644 --- a/.github/workflows/keyboard_plugin_test.yml +++ b/.github/workflows/keyboard_plugin_test.yml @@ -25,12 +25,12 @@ jobs: steps: - name: Checkout core Blockly - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: path: core-blockly - name: Checkout keyboard navigation plugin - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: 'google/blockly-keyboard-experimentation' ref: 'main' diff --git a/core/block.ts b/core/block.ts index d15700a7e19..af44facda5d 100644 --- a/core/block.ts +++ b/core/block.ts @@ -1126,7 +1126,7 @@ export class Block { /** * Returns a generator that provides every field on the block. * - * @yields A generator that can be used to iterate the fields on the block. + * @returns A generator that can be used to iterate the fields on the block. */ *getFields(): Generator { for (const input of this.inputList) { diff --git a/core/block_svg.ts b/core/block_svg.ts index c6065282a5c..b3fdeb2d6b6 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -1844,6 +1844,9 @@ export class BlockSvg /** See IFocusableNode.onNodeFocus. */ onNodeFocus(): void { this.select(); + this.workspace.scrollBoundsIntoView( + this.getBoundingRectangleWithoutChildren(), + ); } /** See IFocusableNode.onNodeBlur. */ diff --git a/core/bubbles/bubble.ts b/core/bubbles/bubble.ts index c42e602544e..742d300adf1 100644 --- a/core/bubbles/bubble.ts +++ b/core/bubbles/bubble.ts @@ -707,6 +707,10 @@ export abstract class Bubble implements IBubble, ISelectable, IFocusableNode { onNodeFocus(): void { this.select(); this.bringToFront(); + const xy = this.getRelativeToSurfaceXY(); + const size = this.getSize(); + const bounds = new Rect(xy.y, xy.y + size.height, xy.x, xy.x + size.width); + this.workspace.scrollBoundsIntoView(bounds); } /** See IFocusableNode.onNodeBlur. */ diff --git a/core/clipboard/block_paster.ts b/core/clipboard/block_paster.ts index 750cedca124..e782cc0b004 100644 --- a/core/clipboard/block_paster.ts +++ b/core/clipboard/block_paster.ts @@ -83,6 +83,9 @@ export function moveBlockToNotConflict( block: BlockSvg, originalPosition: Coordinate, ) { + if (block.workspace.RTL) { + originalPosition.x = block.workspace.getWidth() - originalPosition.x; + } const workspace = block.workspace; const snapRadius = config.snapRadius; const bumpOffset = Coordinate.difference( diff --git a/core/comments/comment_bar_button.ts b/core/comments/comment_bar_button.ts index 24a084ad26e..be130b0e335 100644 --- a/core/comments/comment_bar_button.ts +++ b/core/comments/comment_bar_button.ts @@ -87,7 +87,13 @@ export abstract class CommentBarButton implements IFocusableNode { } /** Called when this button's focusable DOM element gains focus. */ - onNodeFocus() {} + onNodeFocus() { + const commentView = this.getCommentView(); + const xy = commentView.getRelativeToSurfaceXY(); + const size = commentView.getSize(); + const bounds = new Rect(xy.y, xy.y + size.height, xy.x, xy.x + size.width); + commentView.workspace.scrollBoundsIntoView(bounds); + } /** Called when this button's focusable DOM element loses focus. */ onNodeBlur() {} diff --git a/core/comments/comment_editor.ts b/core/comments/comment_editor.ts index ac1559c4b3d..b4c741ba1ad 100644 --- a/core/comments/comment_editor.ts +++ b/core/comments/comment_editor.ts @@ -10,8 +10,10 @@ import {IFocusableNode} from '../interfaces/i_focusable_node.js'; import {IFocusableTree} from '../interfaces/i_focusable_tree.js'; import * as touch from '../touch.js'; import * as dom from '../utils/dom.js'; +import {Rect} from '../utils/rect.js'; import {Size} from '../utils/size.js'; import {Svg} from '../utils/svg.js'; +import * as svgMath from '../utils/svg_math.js'; import {WorkspaceSvg} from '../workspace_svg.js'; /** @@ -188,7 +190,16 @@ export class CommentEditor implements IFocusableNode { getFocusableTree(): IFocusableTree { return this.workspace; } - onNodeFocus(): void {} + onNodeFocus(): void { + const bbox = Rect.from(this.foreignObject.getBoundingClientRect()); + this.workspace.scrollBoundsIntoView( + Rect.createFromPoint( + svgMath.screenToWsCoordinates(this.workspace, bbox.getOrigin()), + bbox.getWidth(), + bbox.getHeight(), + ), + ); + } onNodeBlur(): void {} canBeFocused(): boolean { if (this.id) return true; diff --git a/core/comments/comment_view.ts b/core/comments/comment_view.ts index ca0c261c390..b1cd628f8dd 100644 --- a/core/comments/comment_view.ts +++ b/core/comments/comment_view.ts @@ -368,7 +368,10 @@ export class CommentView implements IRenderedElement { const textPreviewWidth = size.width - foldoutSize.getWidth() - deleteSize.getWidth(); - this.textPreview.setAttribute('x', `${foldoutSize.getWidth()}`); + this.textPreview.setAttribute( + 'x', + `${(this.workspace.RTL ? -1 : 1) * foldoutSize.getWidth()}`, + ); this.textPreview.setAttribute( 'y', `${textPreviewMargin + textPreviewSize.height / 2}`, diff --git a/core/comments/rendered_workspace_comment.ts b/core/comments/rendered_workspace_comment.ts index 49c75e60883..2903bff4bce 100644 --- a/core/comments/rendered_workspace_comment.ts +++ b/core/comments/rendered_workspace_comment.ts @@ -347,6 +347,7 @@ export class RenderedWorkspaceComment this.select(); // Ensure that the comment is always at the top when focused. this.workspace.getLayerManager()?.append(this, layers.BLOCK); + this.workspace.scrollBoundsIntoView(this.getBoundingRectangle()); } /** See IFocusableNode.onNodeBlur. */ diff --git a/core/css.ts b/core/css.ts index 30ee47fc58a..503b6362ba2 100644 --- a/core/css.ts +++ b/core/css.ts @@ -181,7 +181,8 @@ let content = ` cursor: -webkit-grabbing; } -.blocklyDragging.blocklyDraggingDelete { +.blocklyDragging.blocklyDraggingDelete, +.blocklyDragging.blocklyDraggingDelete .blocklyField { cursor: url("<<>>/handdelete.cur"), auto; } diff --git a/core/field.ts b/core/field.ts index fdcb2d693b9..e025efab709 100644 --- a/core/field.ts +++ b/core/field.ts @@ -119,9 +119,6 @@ export abstract class Field return this.size; } - /** - * Sets the size of this field. - */ protected set size_(newValue: Size) { this.size = newValue; } @@ -852,8 +849,7 @@ export abstract class Field totalHeight = Math.max(totalHeight, constants!.FIELD_BORDER_RECT_HEIGHT); } - this.size_.height = totalHeight; - this.size_.width = totalWidth; + this.size_ = new Size(totalWidth, totalHeight); this.positionTextElement_(xOffset, contentWidth); this.positionBorderRect_(); @@ -1384,7 +1380,12 @@ export abstract class Field } /** See IFocusableNode.onNodeFocus. */ - onNodeFocus(): void {} + onNodeFocus(): void { + const block = this.getSourceBlock() as BlockSvg; + block.workspace.scrollBoundsIntoView( + block.getBoundingRectangleWithoutChildren(), + ); + } /** See IFocusableNode.onNodeBlur. */ onNodeBlur(): void {} diff --git a/core/field_dropdown.ts b/core/field_dropdown.ts index 8b01ccddab1..3be5c94c3e3 100644 --- a/core/field_dropdown.ts +++ b/core/field_dropdown.ts @@ -29,6 +29,7 @@ import * as aria from './utils/aria.js'; import {Coordinate} from './utils/coordinate.js'; import * as dom from './utils/dom.js'; import * as parsing from './utils/parsing.js'; +import {Size} from './utils/size.js'; import * as utilsString from './utils/string.js'; import {Svg} from './utils/svg.js'; @@ -553,8 +554,7 @@ export class FieldDropdown extends Field { } else { arrowWidth = dom.getTextWidth(this.arrow as SVGTSpanElement); } - this.size_.width = imageWidth + arrowWidth + xPadding * 2; - this.size_.height = height; + this.size_ = new Size(imageWidth + arrowWidth + xPadding * 2, height); let arrowX = 0; if (block.RTL) { @@ -595,8 +595,7 @@ export class FieldDropdown extends Field { height / 2 - this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2, ); } - this.size_.width = textWidth + arrowWidth + xPadding * 2; - this.size_.height = height; + this.size_ = new Size(textWidth + arrowWidth + xPadding * 2, height); this.positionTextElement_(xPadding, textWidth); } diff --git a/core/field_input.ts b/core/field_input.ts index b685309183a..55383a4c1d2 100644 --- a/core/field_input.ts +++ b/core/field_input.ts @@ -45,6 +45,11 @@ import type {WorkspaceSvg} from './workspace_svg.js'; */ type InputTypes = string | number; +/** + * The minimum width of an input field. + */ +const MINIMUM_WIDTH = 14; + /** * Abstract class for an editable input field. * @@ -102,11 +107,9 @@ export abstract class FieldInput extends Field< */ override SERIALIZABLE = true; - /** - * Sets the size of this field. Although this appears to be a no-op, it must - * exist since the getter is overridden below. - */ protected override set size_(newValue: Size) { + // Although this appears to be a no-op, it must exist since the getter is + // overridden below. super.size_ = newValue; } @@ -115,8 +118,8 @@ export abstract class FieldInput extends Field< */ protected override get size_() { const s = super.size_; - if (s.width < 14) { - s.width = 14; + if (s.width < MINIMUM_WIDTH) { + s.width = MINIMUM_WIDTH; } return s; @@ -732,6 +735,23 @@ export abstract class FieldInput extends Field< return true; } + /** + * Position a field's text element after a size change. This handles both LTR + * and RTL positioning. + * + * @param xMargin x offset to use when positioning the text element. + * @param contentWidth The content width. + */ + protected override positionTextElement_( + xMargin: number, + contentWidth: number, + ) { + const effectiveWidth = xMargin * 2 + contentWidth; + const delta = + effectiveWidth < MINIMUM_WIDTH ? (MINIMUM_WIDTH - effectiveWidth) / 2 : 0; + super.positionTextElement_(xMargin + delta, contentWidth); + } + /** * Use the `getText_` developer hook to override the field's text * representation. When we're currently editing, return the current HTML value diff --git a/core/field_registry.ts b/core/field_registry.ts index 06bb9acd045..e02ece75c96 100644 --- a/core/field_registry.ts +++ b/core/field_registry.ts @@ -56,11 +56,11 @@ export interface RegistrableField { * @param type The field type name as used in the JSON definition. * @param fieldClass The field class containing a fromJson function that can * construct an instance of the field. - * @throws {Error} if the type name is empty, the field is already registered, - * or the fieldClass is not an object containing a fromJson function. + * @throws {Error} if the type name is empty or the fieldClass is not an object + * containing a fromJson function. */ export function register(type: string, fieldClass: RegistrableField) { - registry.register(registry.Type.FIELD, type, fieldClass); + registry.register(registry.Type.FIELD, type, fieldClass, true); } /** diff --git a/core/flyout_button.ts b/core/flyout_button.ts index c9afb8b0159..5a066a23b11 100644 --- a/core/flyout_button.ts +++ b/core/flyout_button.ts @@ -398,7 +398,11 @@ export class FlyoutButton } /** See IFocusableNode.onNodeFocus. */ - onNodeFocus(): void {} + onNodeFocus(): void { + const xy = this.getPosition(); + const bounds = new Rect(xy.y, xy.y + this.height, xy.x, xy.x + this.width); + this.workspace.scrollBoundsIntoView(bounds); + } /** See IFocusableNode.onNodeBlur. */ onNodeBlur(): void {} diff --git a/core/focus_manager.ts b/core/focus_manager.ts index 02e0591070f..47e4324540d 100644 --- a/core/focus_manager.ts +++ b/core/focus_manager.ts @@ -309,6 +309,8 @@ export class FocusManager { * Note that this may update the specified node's element's tabindex to ensure * that it can be properly read out by screenreaders while focused. * + * The focused node will not be automatically scrolled into view. + * * @param focusableNode The node that should receive active focus. */ focusNode(focusableNode: IFocusableNode): void { @@ -423,6 +425,8 @@ export class FocusManager { * the returned lambda is called. Additionally, only 1 ephemeral focus context * can be active at any given time (attempting to activate more than one * simultaneously will result in an error being thrown). + * + * This method does not scroll the ephemerally focused element into view. */ takeEphemeralFocus( focusableElement: HTMLElement | SVGElement, @@ -439,7 +443,7 @@ export class FocusManager { if (this.focusedNode) { this.passivelyFocusNode(this.focusedNode, null); } - focusableElement.focus(); + focusableElement.focus({preventScroll: true}); let hasFinishedEphemeralFocus = false; return () => { @@ -574,7 +578,7 @@ export class FocusManager { } this.setNodeToVisualActiveFocus(node); - elem.focus(); + elem.focus({preventScroll: true}); } /** diff --git a/core/icons/icon.ts b/core/icons/icon.ts index 8f8ff70fc32..f5f76603875 100644 --- a/core/icons/icon.ts +++ b/core/icons/icon.ts @@ -14,6 +14,7 @@ import * as tooltip from '../tooltip.js'; import {Coordinate} from '../utils/coordinate.js'; import * as dom from '../utils/dom.js'; import * as idGenerator from '../utils/idgenerator.js'; +import {Rect} from '../utils/rect.js'; import {Size} from '../utils/size.js'; import {Svg} from '../utils/svg.js'; import type {WorkspaceSvg} from '../workspace_svg.js'; @@ -168,7 +169,16 @@ export abstract class Icon implements IIcon { } /** See IFocusableNode.onNodeFocus. */ - onNodeFocus(): void {} + onNodeFocus(): void { + const blockBounds = (this.sourceBlock as BlockSvg).getBoundingRectangle(); + const bounds = new Rect( + blockBounds.top + this.offsetInBlock.y, + blockBounds.top + this.offsetInBlock.y + this.getSize().height, + blockBounds.left + this.offsetInBlock.x, + blockBounds.left + this.offsetInBlock.x + this.getSize().width, + ); + (this.sourceBlock as BlockSvg).workspace.scrollBoundsIntoView(bounds); + } /** See IFocusableNode.onNodeBlur. */ onNodeBlur(): void {} diff --git a/core/interfaces/i_focusable_node.ts b/core/interfaces/i_focusable_node.ts index 24833328d7f..57ec1a126e1 100644 --- a/core/interfaces/i_focusable_node.ts +++ b/core/interfaces/i_focusable_node.ts @@ -59,6 +59,9 @@ export interface IFocusableNode { * they should avoid the following: * - Creating or removing DOM elements (including via the renderer or drawer). * - Affecting focus via DOM focus() calls or the FocusManager. + * + * Implementations may consider scrolling themselves into view here; that is + * not handled by the focus manager. */ onNodeFocus(): void; diff --git a/core/keyboard_nav/line_cursor.ts b/core/keyboard_nav/line_cursor.ts index 13e5a729d0f..30770e47d2d 100644 --- a/core/keyboard_nav/line_cursor.ts +++ b/core/keyboard_nav/line_cursor.ts @@ -14,14 +14,11 @@ */ import {BlockSvg} from '../block_svg.js'; -import {CommentBarButton} from '../comments/comment_bar_button.js'; import {RenderedWorkspaceComment} from '../comments/rendered_workspace_comment.js'; -import {Field} from '../field.js'; import {getFocusManager} from '../focus_manager.js'; import type {IFocusableNode} from '../interfaces/i_focusable_node.js'; import * as registry from '../registry.js'; -import {Rect} from '../utils/rect.js'; -import {WorkspaceSvg} from '../workspace_svg.js'; +import type {WorkspaceSvg} from '../workspace_svg.js'; import {Marker} from './marker.js'; /** @@ -392,31 +389,6 @@ export class LineCursor extends Marker { */ setCurNode(newNode: IFocusableNode) { getFocusManager().focusNode(newNode); - - // Try to scroll cursor into view. - if (newNode instanceof BlockSvg) { - newNode.workspace.scrollBoundsIntoView( - newNode.getBoundingRectangleWithoutChildren(), - ); - } else if (newNode instanceof Field) { - const block = newNode.getSourceBlock() as BlockSvg; - block.workspace.scrollBoundsIntoView( - block.getBoundingRectangleWithoutChildren(), - ); - } else if (newNode instanceof RenderedWorkspaceComment) { - newNode.workspace.scrollBoundsIntoView(newNode.getBoundingRectangle()); - } else if (newNode instanceof CommentBarButton) { - const commentView = newNode.getCommentView(); - const xy = commentView.getRelativeToSurfaceXY(); - const size = commentView.getSize(); - const bounds = new Rect( - xy.y, - xy.y + size.height, - xy.x, - xy.x + size.width, - ); - commentView.workspace.scrollBoundsIntoView(bounds); - } } /** diff --git a/core/rendered_connection.ts b/core/rendered_connection.ts index 84905eeccc2..4a53048bc84 100644 --- a/core/rendered_connection.ts +++ b/core/rendered_connection.ts @@ -644,6 +644,9 @@ export class RenderedConnection /** See IFocusableNode.onNodeFocus. */ onNodeFocus(): void { this.highlight(); + this.getSourceBlock().workspace.scrollBoundsIntoView( + this.getSourceBlock().getBoundingRectangleWithoutChildren(), + ); } /** See IFocusableNode.onNodeBlur. */ @@ -656,12 +659,12 @@ export class RenderedConnection return true; } - private findHighlightSvg(): SVGElement | null { + private findHighlightSvg(): SVGPathElement | null { // This cast is valid as TypeScript's definition is wrong. See: // https://github.com/microsoft/TypeScript/issues/60996. return document.getElementById(this.id) as | unknown - | null as SVGElement | null; + | null as SVGPathElement | null; } } diff --git a/core/renderers/common/drawer.ts b/core/renderers/common/drawer.ts index 7046406adc7..c474bc8c339 100644 --- a/core/renderers/common/drawer.ts +++ b/core/renderers/common/drawer.ts @@ -122,9 +122,12 @@ export class Drawer { } else if (Types.isSpacer(elem)) { this.outlinePath_ += svgPaths.lineOnAxis('h', elem.width); } + // No branch for a square corner, because it's a no-op. } - // No branch for a square corner, because it's a no-op. - this.outlinePath_ += svgPaths.lineOnAxis('v', topRow.height); + this.outlinePath_ += svgPaths.lineOnAxis( + 'v', + topRow.height - topRow.ascenderHeight, + ); } /** diff --git a/core/touch.ts b/core/touch.ts index 8fb2cd2298c..9af3b1f9494 100644 --- a/core/touch.ts +++ b/core/touch.ts @@ -46,6 +46,7 @@ export const TOUCH_MAP: {[key: string]: string[]} = { 'mouseup': ['pointerup', 'pointercancel'], 'touchend': ['pointerup'], 'touchcancel': ['pointercancel'], + 'pointerup': ['pointerup', 'pointercancel'], }; /** PID of queued long-press task. */ diff --git a/core/xml.ts b/core/xml.ts index cc26d8c8a2c..362a59ab287 100644 --- a/core/xml.ts +++ b/core/xml.ts @@ -68,7 +68,7 @@ export function saveWorkspaceComment( if (!skipId) elem.setAttribute('id', comment.id); const workspace = comment.workspace; - const loc = comment.getRelativeToSurfaceXY(); + const loc = comment.getRelativeToSurfaceXY().clone(); loc.x = workspace.RTL ? workspace.getWidth() - loc.x : loc.x; elem.setAttribute('x', `${loc.x}`); elem.setAttribute('y', `${loc.y}`); diff --git a/eslint.config.mjs b/eslint.config.mjs index f018e525d87..744e02b45bf 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,6 +1,7 @@ import eslint from '@eslint/js'; import googleStyle from 'eslint-config-google'; import jsdoc from 'eslint-plugin-jsdoc'; +import mochaPlugin from 'eslint-plugin-mocha'; import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; import globals from 'globals'; import tseslint from 'typescript-eslint'; @@ -88,7 +89,8 @@ function buildTSOverride({files, tsconfig}) { '@typescript-eslint/no-explicit-any': ['off'], // We use this pattern extensively for block (e.g. controls_if) interfaces. '@typescript-eslint/no-empty-object-type': ['off'], - + // TSDoc doesn't support @yields, so don't require that we use it. + 'jsdoc/require-yields': ['off'], // params and returns docs are optional. 'jsdoc/require-param-description': ['off'], 'jsdoc/require-returns': ['off'], @@ -200,6 +202,9 @@ export default [ }, { files: ['tests/**'], + plugins: { + mocha: mochaPlugin, + }, languageOptions: { globals: { 'Blockly': true, @@ -219,6 +224,7 @@ export default [ 'jsdoc/check-tag-names': ['warn', {'definedTags': ['record']}], 'jsdoc/tag-lines': ['off'], 'jsdoc/no-defaults': ['off'], + 'mocha/no-exclusive-tests': 'error', }, }, { diff --git a/gulpfile.mjs b/gulpfile.mjs index fd3de3bde8c..ad61bcb516d 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -45,7 +45,11 @@ import { publishBeta, recompile, } from './scripts/gulpfiles/release_tasks.mjs'; -import {generators, test} from './scripts/gulpfiles/test_tasks.mjs'; +import { + generators, + interactiveMocha, + test, +} from './scripts/gulpfiles/test_tasks.mjs'; const clean = parallel(cleanBuildDir, cleanReleaseDir); @@ -80,6 +84,7 @@ export { clean, test, generators as testGenerators, + interactiveMocha, buildAdvancedCompilationTest, createRC as gitCreateRC, docs, diff --git a/package-lock.json b/package-lock.json index e03f6d24d46..afd057ce613 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "blockly", - "version": "12.2.0", + "version": "12.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "blockly", - "version": "12.2.0", + "version": "12.3.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -22,12 +22,13 @@ "@microsoft/api-extractor": "^7.29.5", "ajv": "^8.17.1", "async-done": "^2.0.0", - "chai": "^5.1.1", + "chai": "^6.0.1", "concurrently": "^9.0.1", "eslint": "^9.15.0", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^10.1.1", "eslint-plugin-jsdoc": "^52.0.2", + "eslint-plugin-mocha": "^11.1.0", "eslint-plugin-prettier": "^5.2.1", "glob": "^11.0.1", "globals": "^16.0.0", @@ -50,6 +51,7 @@ "patch-package": "^8.0.0", "prettier": "^3.3.3", "prettier-plugin-organize-imports": "^4.0.0", + "puppeteer-core": "^24.17.0", "readline-sync": "^1.4.10", "rimraf": "^5.0.0", "typescript": "^5.3.3", @@ -444,19 +446,21 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -526,10 +530,11 @@ "license": "MIT" }, "node_modules/@eslint/js": { - "version": "9.30.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.0.tgz", - "integrity": "sha512-Wzw3wQwPvc9sHM+NjakWTcPx11mbZyiYHuwWa/QfZ7cIRX7WK54PSk7bdyXDaoaopUcMatv1zaQvOAAO8hCdww==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -547,30 +552,19 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@gulp-sourcemaps/identity-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", @@ -1289,18 +1283,18 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.10.4", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.4.tgz", - "integrity": "sha512-9DxbZx+XGMNdjBynIs4BRSz+M3iRDeB7qRcAr6UORFLphCIM2x3DXgOucvADiifcqCE4XePFUKcnaAMyGbrDlQ==", + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.7.tgz", + "integrity": "sha512-wHWLkQWBjHtajZeqCB74nsa/X70KheyOhySYBRmVQDJiNj0zjZR/naPCvdWjMhcG1LmjaMV/9WtTo5mpe8qWLw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "debug": "^4.4.0", + "debug": "^4.4.1", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", - "semver": "^7.7.1", - "tar-fs": "^3.0.8", + "semver": "^7.7.2", + "tar-fs": "^3.1.0", "yargs": "^17.7.2" }, "bin": { @@ -1527,7 +1521,8 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { "version": "20.16.3", @@ -2488,15 +2483,6 @@ "node": ">=0.10.0" } }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -2881,18 +2867,11 @@ } }, "node_modules/chai": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", - "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.0.1.tgz", + "integrity": "sha512-/JOoU2//6p5vCXh00FpNgtlw0LjvhGttaWc+y7wpW9yjBm3ys0dI8tSKZxIOgNruz5J0RleccatSIC3uxEZP0g==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, "engines": { "node": ">=18" } @@ -2913,15 +2892,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "engines": { - "node": ">= 16" - } - }, "node_modules/cheerio": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", @@ -2991,6 +2961,20 @@ "fsevents": "~2.3.2" } }, + "node_modules/chromium-bidi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-8.0.0.tgz", + "integrity": "sha512-d1VmE0FD7lxZQHzcDUCKZSNRtRwISXDsdg4HjdTR5+Ll5nQ/vzU12JeNmupD6VWffrPSlrnGhEWlLESKH3VO+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, "node_modules/ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -3585,15 +3569,6 @@ "node": ">=0.10" } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3652,6 +3627,13 @@ "node": ">=0.10.0" } }, + "node_modules/devtools-protocol": { + "version": "0.0.1475386", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1475386.tgz", + "integrity": "sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/diff": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", @@ -4003,19 +3985,20 @@ } }, "node_modules/eslint": { - "version": "9.30.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.0.tgz", - "integrity": "sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -4075,10 +4058,11 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, + "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -4137,11 +4121,37 @@ "spdx-license-ids": "^3.0.0" } }, + "node_modules/eslint-plugin-mocha": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-11.1.0.tgz", + "integrity": "sha512-rKntVWRsQFPbf8OkSgVNRVRrcVAPaGTyEgWCEyXaPDJkTl0v5/lwu1vTk5sWiUJU8l2sxwvGUZzSNrEKdVMeQw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.1", + "globals": "^15.14.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/eslint-plugin-mocha/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, + "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.11.7" @@ -6766,15 +6776,6 @@ "dev": true, "license": "MIT" }, - "node_modules/loupe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", - "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -6916,6 +6917,13 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -7703,15 +7711,6 @@ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "engines": { - "node": ">= 14.16" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -7955,6 +7954,24 @@ "node": ">=6" } }, + "node_modules/puppeteer-core": { + "version": "24.17.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.17.0.tgz", + "integrity": "sha512-RYOBKFiF+3RdwIZTEacqNpD567gaFcBAOKTT7742FdB1icXudrPI7BlZbYTYWK2wgGQUXt9Zi1Yn+D5PmCs4CA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.10.7", + "chromium-bidi": "8.0.0", + "debug": "^4.4.1", + "devtools-protocol": "0.0.1475386", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", @@ -9023,9 +9040,9 @@ } }, "node_modules/tar-fs": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.9.tgz", - "integrity": "sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz", + "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==", "dev": true, "license": "MIT", "dependencies": { @@ -9242,6 +9259,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "dev": true, + "license": "MIT" + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -9803,9 +9827,10 @@ "dev": true }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -10059,6 +10084,16 @@ "dependencies": { "safe-buffer": "~5.2.0" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index e7a496b31f8..7b639a872ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blockly", - "version": "12.2.0", + "version": "12.3.0", "description": "Blockly is a library for building visual programming editors.", "keywords": [ "blockly" @@ -45,7 +45,7 @@ "test": "gulp test", "test:browser": "cd tests/browser && npx mocha", "test:generators": "gulp testGenerators", - "test:mocha:interactive": "npm run build && concurrently -n tsc,server \"tsc --watch --preserveWatchOutput --outDir \"build/src\" --declarationDir \"build/declarations\"\" \"http-server ./ -o /tests/mocha/index.html -c-1\"", + "test:mocha:interactive": "npm run build && concurrently -n tsc,server \"tsc --watch --preserveWatchOutput --outDir \"build/src\" --declarationDir \"build/declarations\"\" \"gulp interactiveMocha\"", "test:compile:advanced": "gulp buildAdvancedCompilationTest --debug", "updateGithubPages": "npm ci && gulp gitUpdateGithubPages" }, @@ -109,12 +109,13 @@ "@microsoft/api-extractor": "^7.29.5", "ajv": "^8.17.1", "async-done": "^2.0.0", - "chai": "^5.1.1", + "chai": "^6.0.1", "concurrently": "^9.0.1", "eslint": "^9.15.0", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^10.1.1", "eslint-plugin-jsdoc": "^52.0.2", + "eslint-plugin-mocha": "^11.1.0", "eslint-plugin-prettier": "^5.2.1", "glob": "^11.0.1", "globals": "^16.0.0", @@ -137,6 +138,7 @@ "patch-package": "^8.0.0", "prettier": "^3.3.3", "prettier-plugin-organize-imports": "^4.0.0", + "puppeteer-core": "^24.17.0", "readline-sync": "^1.4.10", "rimraf": "^5.0.0", "typescript": "^5.3.3", diff --git a/scripts/gulpfiles/test_tasks.mjs b/scripts/gulpfiles/test_tasks.mjs index d4b73cdb3c1..37f9884440d 100644 --- a/scripts/gulpfiles/test_tasks.mjs +++ b/scripts/gulpfiles/test_tasks.mjs @@ -257,9 +257,9 @@ async function metadata() { * Run Mocha tests inside a browser. * @return {Promise} Asynchronous result. */ -async function mocha() { +async function mocha(exitOnCompletion = true) { return runTestTask('mocha', async () => { - const result = await runMochaTestsInBrowser().catch(e => { + const result = await runMochaTestsInBrowser(exitOnCompletion).catch(e => { throw e; }); if (result) { @@ -269,6 +269,14 @@ async function mocha() { }); } +/** + * Run Mocha tests inside a browser and keep the browser open upon completion. + * @return {Promise} Asynchronous result. + */ +export async function interactiveMocha() { + return mocha(false); +} + /** * Helper method for comparison file. * @param {string} file1 First target file. diff --git a/tests/browser/test/basic_playground_test.mjs b/tests/browser/test/basic_playground_test.mjs index 4c54523bd7f..72b3894a6a3 100644 --- a/tests/browser/test/basic_playground_test.mjs +++ b/tests/browser/test/basic_playground_test.mjs @@ -101,6 +101,45 @@ suite('Right Clicking on Blocks', function () { await contextMenuSelect(this.browser, this.block, 'Remove Comment'); chai.assert.isNull(await getCommentText(this.browser, this.block.id)); }); + + test('does not scroll the page when node is ephemerally focused', async function () { + const initialScroll = await this.browser.execute(() => { + return window.scrollY; + }); + // This left-right-left sequence was necessary to reproduce unintended + // scrolling; regardless of the number of clicks/context menu activations, + // the page should not scroll. + this.block.click({button: 2}); + this.block.click({button: 0}); + this.block.click({button: 2}); + await this.browser.pause(250); + const finalScroll = await this.browser.execute(() => { + return window.scrollY; + }); + + chai.assert.equal(initialScroll, finalScroll); + }); + + test('does not scroll the page when node is actively focused', async function () { + await this.browser.setWindowSize(500, 300); + await this.browser.setViewport({width: 500, height: 300}); + const initialScroll = await this.browser.execute((blockId) => { + window.scrollTo(0, document.body.scrollHeight); + return window.scrollY; + }, this.block.id); + await this.browser.execute(() => { + Blockly.getFocusManager().focusNode( + Blockly.getMainWorkspace().getToolbox(), + ); + }); + const finalScroll = await this.browser.execute(() => { + return window.scrollY; + }); + + chai.assert.equal(initialScroll, finalScroll); + await this.browser.setWindowSize(800, 600); + await this.browser.setViewport({width: 800, height: 600}); + }); }); suite('Disabling', function () { @@ -199,3 +238,224 @@ suite('Disabling', function () { }, ); }); + +suite('Focused nodes are scrolled into bounds', function () { + // Setting timeout to unlimited as the webdriver takes a longer time to run + // than most mocha tests. + this.timeout(0); + + // Setup Selenium for all of the tests + suiteSetup(async function () { + this.browser = await testSetup(testFileLocations.PLAYGROUND); + await this.browser.execute(() => { + window.focusScrollTest = async (testcase) => { + const workspace = Blockly.getMainWorkspace(); + const metrics = workspace.getMetricsManager(); + const initialViewport = metrics.getViewMetrics(true); + const elementBounds = await testcase(workspace); + await Blockly.renderManagement.finishQueuedRenders(); + const scrolledViewport = metrics.getViewMetrics(true); + const workspaceBounds = new Blockly.utils.Rect( + scrolledViewport.top, + scrolledViewport.top + scrolledViewport.height, + scrolledViewport.left, + scrolledViewport.left + scrolledViewport.width, + ); + return { + changed: + JSON.stringify(initialViewport) !== + JSON.stringify(scrolledViewport), + intersects: elementBounds.intersects(workspaceBounds), + contains: workspaceBounds.contains( + elementBounds.getOrigin().x, + elementBounds.getOrigin().y, + ), + elementBounds, + workspaceBounds, + }; + }; + }); + }); + + setup(async function () { + await this.browser.execute(() => { + Blockly.serialization.blocks.append( + { + 'type': 'text', + 'x': -500, + 'y': -500, + }, + Blockly.getMainWorkspace(), + ); + Blockly.serialization.blocks.append( + { + 'type': 'controls_if', + 'x': 500, + 'y': 500, + }, + Blockly.getMainWorkspace(), + ); + Blockly.getMainWorkspace().zoomCenter(1); + }); + }); + + test('Focused blocks scroll into bounds', async function () { + const result = await this.browser.execute(async () => { + return await window.focusScrollTest(async (workspace) => { + const block = workspace.getTopBlocks()[0]; + Blockly.getFocusManager().focusNode(block); + return block.getBoundingRectangleWithoutChildren(); + }); + }); + chai.assert.isTrue(result.intersects); + chai.assert.isTrue(result.contains); + chai.assert.isTrue(result.changed); + }); + + test('Focused bubbles scroll into bounds', async function () { + const result = await this.browser.execute(async () => { + return await window.focusScrollTest(async (workspace) => { + const block = workspace.getTopBlocks()[0]; + block.setCommentText('hello world'); + const icon = block.getIcon(Blockly.icons.IconType.COMMENT); + icon.setBubbleVisible(true); + await Blockly.renderManagement.finishQueuedRenders(); + icon.setBubbleLocation(new Blockly.utils.Coordinate(-510, -510)); + Blockly.getFocusManager().focusNode(icon.getBubble()); + const xy = icon.getBubble().getRelativeToSurfaceXY(); + const size = icon.getBubble().getSize(); + return new Blockly.utils.Rect( + xy.y, + xy.y + size.height, + xy.x, + xy.x + size.width, + ); + }); + }); + + chai.assert.isTrue(result.intersects); + chai.assert.isTrue(result.contains); + chai.assert.isTrue(result.changed); + }); + + test('Comment bar buttons scroll into bounds', async function () { + const result = await this.browser.execute(async () => { + return await window.focusScrollTest(async (workspace) => { + const comment = new Blockly.comments.RenderedWorkspaceComment( + workspace, + ); + comment.moveTo(new Blockly.utils.Coordinate(-300, 500)); + const commentBarButton = comment.view.getCommentBarButtons()[0]; + Blockly.getFocusManager().focusNode(commentBarButton); + const xy = comment.view.getRelativeToSurfaceXY(); + const size = comment.view.getSize(); + // Comment bar buttons scroll their parent comment view into view. + return new Blockly.utils.Rect( + xy.y, + xy.y + size.height, + xy.x, + xy.x + size.width, + ); + }); + }); + + chai.assert.isTrue(result.intersects); + chai.assert.isTrue(result.contains); + chai.assert.isTrue(result.changed); + }); + + test('Comment editors scroll into bounds', async function () { + const result = await this.browser.execute(async () => { + return await window.focusScrollTest(async (workspace) => { + const comment = new Blockly.comments.RenderedWorkspaceComment( + workspace, + ); + comment.moveTo(new Blockly.utils.Coordinate(-300, 500)); + const commentEditor = comment.view.getEditorFocusableNode(); + Blockly.getFocusManager().focusNode(commentEditor); + // Comment editor bounds can't be calculated externally since they + // depend on private properties, but the comment view is a reasonable + // proxy. + const xy = comment.view.getRelativeToSurfaceXY(); + const size = comment.view.getSize(); + return new Blockly.utils.Rect( + xy.y, + xy.y + size.height, + xy.x, + xy.x + size.width, + ); + }); + }); + + chai.assert.isTrue(result.intersects); + chai.assert.isTrue(result.contains); + chai.assert.isTrue(result.changed); + }); + + test('Workspace comments scroll into bounds', async function () { + const result = await this.browser.execute(async () => { + return await window.focusScrollTest(async (workspace) => { + const comment = new Blockly.comments.RenderedWorkspaceComment( + workspace, + ); + comment.moveTo(new Blockly.utils.Coordinate(-500, 500)); + Blockly.getFocusManager().focusNode(comment); + return comment.getBoundingRectangle(); + }); + }); + + chai.assert.isTrue(result.intersects); + chai.assert.isTrue(result.contains); + chai.assert.isTrue(result.changed); + }); + + test('Icons scroll into bounds', async function () { + const result = await this.browser.execute(async () => { + return await window.focusScrollTest(async (workspace) => { + const block = workspace.getTopBlocks()[0]; + block.setWarningText('this is bad'); + const icon = block.getIcon(Blockly.icons.IconType.WARNING); + Blockly.getFocusManager().focusNode(icon); + // Icon bounds can't be calculated externally since they depend on + // protected properties, but the parent block is a reasonable proxy. + return block.getBoundingRectangleWithoutChildren(); + }); + }); + + chai.assert.isTrue(result.intersects); + chai.assert.isTrue(result.contains); + chai.assert.isTrue(result.changed); + }); + + test('Fields scroll into bounds', async function () { + const result = await this.browser.execute(async () => { + return await window.focusScrollTest(async (workspace) => { + const block = workspace.getTopBlocks()[0]; + const field = block.getField('TEXT'); + Blockly.getFocusManager().focusNode(field); + // Fields scroll their source block into view. + return block.getBoundingRectangleWithoutChildren(); + }); + }); + + chai.assert.isTrue(result.intersects); + chai.assert.isTrue(result.contains); + chai.assert.isTrue(result.changed); + }); + + test('Connections scroll into bounds', async function () { + const result = await this.browser.execute(async () => { + return await window.focusScrollTest(async (workspace) => { + const block = workspace.getBlocksByType('controls_if')[0]; + Blockly.getFocusManager().focusNode(block.nextConnection); + // Connection bounds can't be calculated externally since they depend on + // protected properties, but the parent block is a reasonable proxy. + return block.getBoundingRectangleWithoutChildren(); + }); + }); + + chai.assert.isTrue(result.intersects); + chai.assert.isTrue(result.contains); + chai.assert.isTrue(result.changed); + }); +}); diff --git a/tests/mocha/block_json_test.js b/tests/mocha/block_json_test.js index 4baccef6b7b..31abd6e3484 100644 --- a/tests/mocha/block_json_test.js +++ b/tests/mocha/block_json_test.js @@ -5,7 +5,7 @@ */ import {Align} from '../../build/src/core/inputs/align.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/block_test.js b/tests/mocha/block_test.js index e3bd470902d..1f8f9b1ee41 100644 --- a/tests/mocha/block_test.js +++ b/tests/mocha/block_test.js @@ -11,7 +11,7 @@ import {IconType} from '../../build/src/core/icons/icon_types.js'; import {EndRowInput} from '../../build/src/core/inputs/end_row_input.js'; import {isCommentIcon} from '../../build/src/core/interfaces/i_comment_icon.js'; import {Size} from '../../build/src/core/utils/size.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {createRenderedBlock} from './test_helpers/block_definitions.js'; import { createChangeListenerSpy, diff --git a/tests/mocha/blocks/lists_test.js b/tests/mocha/blocks/lists_test.js index 490109d22ca..e749fae90a7 100644 --- a/tests/mocha/blocks/lists_test.js +++ b/tests/mocha/blocks/lists_test.js @@ -5,7 +5,7 @@ */ import {ConnectionType} from '../../../build/src/core/connection_type.js'; -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; import {defineStatementBlock} from '../test_helpers/block_definitions.js'; import {runSerializationTestSuite} from '../test_helpers/serialization.js'; import { diff --git a/tests/mocha/blocks/logic_ternary_test.js b/tests/mocha/blocks/logic_ternary_test.js index 71920935981..3d343a7caec 100644 --- a/tests/mocha/blocks/logic_ternary_test.js +++ b/tests/mocha/blocks/logic_ternary_test.js @@ -5,7 +5,7 @@ */ import * as eventUtils from '../../../build/src/core/events/utils.js'; -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; import {runSerializationTestSuite} from '../test_helpers/serialization.js'; import { sharedTestSetup, diff --git a/tests/mocha/blocks/loops_test.js b/tests/mocha/blocks/loops_test.js index f8d74916c29..eb040c884a7 100644 --- a/tests/mocha/blocks/loops_test.js +++ b/tests/mocha/blocks/loops_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../../build/src/core/blockly.js'; -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/blocks/procedures_test.js b/tests/mocha/blocks/procedures_test.js index 4b20662cf93..5ac651fe4cd 100644 --- a/tests/mocha/blocks/procedures_test.js +++ b/tests/mocha/blocks/procedures_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../../build/src/core/blockly.js'; -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; import {defineRowBlock} from '../test_helpers/block_definitions.js'; import { assertCallBlockStructure, diff --git a/tests/mocha/blocks/variables_test.js b/tests/mocha/blocks/variables_test.js index d12691dd476..a317fe11b52 100644 --- a/tests/mocha/blocks/variables_test.js +++ b/tests/mocha/blocks/variables_test.js @@ -5,7 +5,7 @@ */ import {nameUsedWithConflictingParam} from '../../../build/src/core/variables.js'; -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; import { MockParameterModelWithVar, MockProcedureModel, diff --git a/tests/mocha/clipboard_test.js b/tests/mocha/clipboard_test.js index d58f78b9b50..ff49c0e303c 100644 --- a/tests/mocha/clipboard_test.js +++ b/tests/mocha/clipboard_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { assertEventFired, createChangeListenerSpy, @@ -157,6 +157,34 @@ suite('Clipboard', function () { ); }); + test('pasted blocks are bumped to not overlap in RTL', function () { + this.workspace.dispose(); + this.workspace = Blockly.inject('blocklyDiv', {rtl: true}); + const block = Blockly.serialization.blocks.append( + { + 'type': 'controls_if', + 'x': 38, + 'y': 13, + }, + this.workspace, + ); + const data = block.toCopyData(); + + const newBlock = Blockly.clipboard.paste(data, this.workspace); + const oldBlockXY = block.getRelativeToSurfaceXY(); + assert.deepEqual( + newBlock.getRelativeToSurfaceXY(), + new Blockly.utils.Coordinate( + oldBlockXY.x - Blockly.config.snapRadius, + oldBlockXY.y + Blockly.config.snapRadius * 2, + ), + ); + + // Restore an LTR workspace. + this.workspace.dispose(); + this.workspace = Blockly.inject('blocklyDiv'); + }); + test('pasted blocks are bumped to be outside the connection snap radius', function () { Blockly.serialization.workspaces.load( { @@ -208,5 +236,28 @@ suite('Clipboard', function () { new Blockly.utils.Coordinate(40, 40), ); }); + + test('pasted comments are bumped to not overlap in RTL', function () { + this.workspace.dispose(); + this.workspace = Blockly.inject('blocklyDiv', {rtl: true}); + Blockly.Xml.domToWorkspace( + Blockly.utils.xml.textToDom( + '', + ), + this.workspace, + ); + const comment = this.workspace.getTopComments(false)[0]; + const data = comment.toCopyData(); + + const newComment = Blockly.clipboard.paste(data, this.workspace); + const oldCommentXY = comment.getRelativeToSurfaceXY(); + assert.deepEqual( + newComment.getRelativeToSurfaceXY(), + new Blockly.utils.Coordinate(oldCommentXY.x - 30, oldCommentXY.y + 30), + ); + // Restore an LTR workspace. + this.workspace.dispose(); + this.workspace = Blockly.inject('blocklyDiv'); + }); }); }); diff --git a/tests/mocha/comment_deserialization_test.js b/tests/mocha/comment_deserialization_test.js index 54ee0b2ff30..f834eb0f301 100644 --- a/tests/mocha/comment_deserialization_test.js +++ b/tests/mocha/comment_deserialization_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/comment_test.js b/tests/mocha/comment_test.js index 0ff1c239e30..1f52df8fd52 100644 --- a/tests/mocha/comment_test.js +++ b/tests/mocha/comment_test.js @@ -5,7 +5,7 @@ */ import {EventType} from '../../build/src/core/events/type.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {assertEventFired} from './test_helpers/events.js'; import { sharedTestSetup, diff --git a/tests/mocha/comment_view_test.js b/tests/mocha/comment_view_test.js index 57a24742457..a60a7a973ff 100644 --- a/tests/mocha/comment_view_test.js +++ b/tests/mocha/comment_view_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/connection_checker_test.js b/tests/mocha/connection_checker_test.js index fee2966d766..bdbcb70a6ec 100644 --- a/tests/mocha/connection_checker_test.js +++ b/tests/mocha/connection_checker_test.js @@ -5,7 +5,7 @@ */ import {ConnectionType} from '../../build/src/core/connection_type.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/connection_db_test.js b/tests/mocha/connection_db_test.js index 04f685124ca..459c59e3ab4 100644 --- a/tests/mocha/connection_db_test.js +++ b/tests/mocha/connection_db_test.js @@ -6,7 +6,7 @@ import {ConnectionType} from '../../build/src/core/connection_type.js'; import * as idGenerator from '../../build/src/core/utils/idgenerator.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/connection_test.js b/tests/mocha/connection_test.js index cefea1784e7..b36f358eac3 100644 --- a/tests/mocha/connection_test.js +++ b/tests/mocha/connection_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { defineRowBlock, defineStackBlock, diff --git a/tests/mocha/contextmenu_items_test.js b/tests/mocha/contextmenu_items_test.js index d9044ec7e28..08ab5d5267b 100644 --- a/tests/mocha/contextmenu_items_test.js +++ b/tests/mocha/contextmenu_items_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/contextmenu_test.js b/tests/mocha/contextmenu_test.js index 65896112bb1..df5bf79dc35 100644 --- a/tests/mocha/contextmenu_test.js +++ b/tests/mocha/contextmenu_test.js @@ -6,7 +6,7 @@ import {callbackFactory} from '../../build/src/core/contextmenu.js'; import * as xmlUtils from '../../build/src/core/utils/xml.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/cursor_test.js b/tests/mocha/cursor_test.js index 6f841ae09c6..02426ae26b8 100644 --- a/tests/mocha/cursor_test.js +++ b/tests/mocha/cursor_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {createRenderedBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/dialog_test.js b/tests/mocha/dialog_test.js index f250ff0f8aa..7d4147d83f8 100644 --- a/tests/mocha/dialog_test.js +++ b/tests/mocha/dialog_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/dropdowndiv_test.js b/tests/mocha/dropdowndiv_test.js index fc792fbaf24..495237f18bc 100644 --- a/tests/mocha/dropdowndiv_test.js +++ b/tests/mocha/dropdowndiv_test.js @@ -6,7 +6,7 @@ import {Rect} from '../../build/src/core/utils/rect.js'; import * as style from '../../build/src/core/utils/style.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_block_change_test.js b/tests/mocha/event_block_change_test.js index 7de0a23b607..9e1f9c3103e 100644 --- a/tests/mocha/event_block_change_test.js +++ b/tests/mocha/event_block_change_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineMutatorBlocks} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/event_block_create_test.js b/tests/mocha/event_block_create_test.js index f59f9435efd..1672b56bb98 100644 --- a/tests/mocha/event_block_create_test.js +++ b/tests/mocha/event_block_create_test.js @@ -5,7 +5,7 @@ */ import {EventType} from '../../build/src/core/events/type.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineRowBlock} from './test_helpers/block_definitions.js'; import {assertEventFired} from './test_helpers/events.js'; import { diff --git a/tests/mocha/event_block_delete_test.js b/tests/mocha/event_block_delete_test.js index d74b6aa062b..e2fb5b8ce88 100644 --- a/tests/mocha/event_block_delete_test.js +++ b/tests/mocha/event_block_delete_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineRowBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/event_block_drag_test.js b/tests/mocha/event_block_drag_test.js index 9b0f2031ad0..cc71e3bf084 100644 --- a/tests/mocha/event_block_drag_test.js +++ b/tests/mocha/event_block_drag_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineRowBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/event_block_field_intermediate_change_test.js b/tests/mocha/event_block_field_intermediate_change_test.js index 0ff4e1bbf3c..d917dadcdd5 100644 --- a/tests/mocha/event_block_field_intermediate_change_test.js +++ b/tests/mocha/event_block_field_intermediate_change_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_block_move_test.js b/tests/mocha/event_block_move_test.js index b93457e14c1..6d1890eebeb 100644 --- a/tests/mocha/event_block_move_test.js +++ b/tests/mocha/event_block_move_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineRowBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/event_bubble_open_test.js b/tests/mocha/event_bubble_open_test.js index 099a625f6e2..a445a6a7819 100644 --- a/tests/mocha/event_bubble_open_test.js +++ b/tests/mocha/event_bubble_open_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineMutatorBlocks} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/event_click_test.js b/tests/mocha/event_click_test.js index 6e18769485b..5c4afbcadf9 100644 --- a/tests/mocha/event_click_test.js +++ b/tests/mocha/event_click_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineRowBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/event_comment_change_test.js b/tests/mocha/event_comment_change_test.js index ed5f4d9f6ae..edb539ef555 100644 --- a/tests/mocha/event_comment_change_test.js +++ b/tests/mocha/event_comment_change_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_comment_collapse_test.js b/tests/mocha/event_comment_collapse_test.js index e2d27530708..5c3f61054a1 100644 --- a/tests/mocha/event_comment_collapse_test.js +++ b/tests/mocha/event_comment_collapse_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_comment_create_test.js b/tests/mocha/event_comment_create_test.js index df919541d95..71ef8ed1b75 100644 --- a/tests/mocha/event_comment_create_test.js +++ b/tests/mocha/event_comment_create_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_comment_delete_test.js b/tests/mocha/event_comment_delete_test.js index 2e2bb45c491..dd9f0dd2286 100644 --- a/tests/mocha/event_comment_delete_test.js +++ b/tests/mocha/event_comment_delete_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_comment_drag_test.js b/tests/mocha/event_comment_drag_test.js index d214e0adba1..f6685cc5bdf 100644 --- a/tests/mocha/event_comment_drag_test.js +++ b/tests/mocha/event_comment_drag_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_comment_move_test.js b/tests/mocha/event_comment_move_test.js index aae3fdfe632..b3acea990a6 100644 --- a/tests/mocha/event_comment_move_test.js +++ b/tests/mocha/event_comment_move_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_comment_resize_test.js b/tests/mocha/event_comment_resize_test.js index b74e1abb2bf..bed3e733a65 100644 --- a/tests/mocha/event_comment_resize_test.js +++ b/tests/mocha/event_comment_resize_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_selected_test.js b/tests/mocha/event_selected_test.js index 1ce8306db48..8731099ec96 100644 --- a/tests/mocha/event_selected_test.js +++ b/tests/mocha/event_selected_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineRowBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/event_test.js b/tests/mocha/event_test.js index 7423f22f74b..a5019c8a978 100644 --- a/tests/mocha/event_test.js +++ b/tests/mocha/event_test.js @@ -6,7 +6,7 @@ import * as Blockly from '../../build/src/core/blockly.js'; import * as eventUtils from '../../build/src/core/events/utils.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { assertEventEquals, assertNthCallEventArgEquals, diff --git a/tests/mocha/event_theme_change_test.js b/tests/mocha/event_theme_change_test.js index f20f745b6a0..396347c9e13 100644 --- a/tests/mocha/event_theme_change_test.js +++ b/tests/mocha/event_theme_change_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_toolbox_item_select_test.js b/tests/mocha/event_toolbox_item_select_test.js index bf6a9a46212..02484c35bc1 100644 --- a/tests/mocha/event_toolbox_item_select_test.js +++ b/tests/mocha/event_toolbox_item_select_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_trashcan_open_test.js b/tests/mocha/event_trashcan_open_test.js index 2c809f2dfad..47da09a075a 100644 --- a/tests/mocha/event_trashcan_open_test.js +++ b/tests/mocha/event_trashcan_open_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_var_create_test.js b/tests/mocha/event_var_create_test.js index e374c496541..79af41281de 100644 --- a/tests/mocha/event_var_create_test.js +++ b/tests/mocha/event_var_create_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_var_delete_test.js b/tests/mocha/event_var_delete_test.js index b06943d9a19..93d9ef0ba2d 100644 --- a/tests/mocha/event_var_delete_test.js +++ b/tests/mocha/event_var_delete_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_var_rename_test.js b/tests/mocha/event_var_rename_test.js index 7fbd185ab7b..b6d77cb35bd 100644 --- a/tests/mocha/event_var_rename_test.js +++ b/tests/mocha/event_var_rename_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_var_type_change_test.js b/tests/mocha/event_var_type_change_test.js index d19b0421a33..066c145a3ef 100644 --- a/tests/mocha/event_var_type_change_test.js +++ b/tests/mocha/event_var_type_change_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/event_viewport_test.js b/tests/mocha/event_viewport_test.js index edacc0da6cb..cd11079fa32 100644 --- a/tests/mocha/event_viewport_test.js +++ b/tests/mocha/event_viewport_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/extensions_test.js b/tests/mocha/extensions_test.js index 66772cbea4b..8c41861d5d8 100644 --- a/tests/mocha/extensions_test.js +++ b/tests/mocha/extensions_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/field_checkbox_test.js b/tests/mocha/field_checkbox_test.js index 08190fed823..74357338a5a 100644 --- a/tests/mocha/field_checkbox_test.js +++ b/tests/mocha/field_checkbox_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineRowBlock} from './test_helpers/block_definitions.js'; import { assertFieldValue, diff --git a/tests/mocha/field_colour_test.js b/tests/mocha/field_colour_test.js index 262f978f29d..975d5a01d4a 100644 --- a/tests/mocha/field_colour_test.js +++ b/tests/mocha/field_colour_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { createTestBlock, defineRowBlock, diff --git a/tests/mocha/field_dropdown_test.js b/tests/mocha/field_dropdown_test.js index e9bc159146d..a1731e81281 100644 --- a/tests/mocha/field_dropdown_test.js +++ b/tests/mocha/field_dropdown_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { createTestBlock, defineRowBlock, diff --git a/tests/mocha/field_image_test.js b/tests/mocha/field_image_test.js index a02b3f6b64a..f0358703bdf 100644 --- a/tests/mocha/field_image_test.js +++ b/tests/mocha/field_image_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { assertFieldValue, runConstructorSuiteTests, diff --git a/tests/mocha/field_label_serializable_test.js b/tests/mocha/field_label_serializable_test.js index a831713412c..443cc6d1753 100644 --- a/tests/mocha/field_label_serializable_test.js +++ b/tests/mocha/field_label_serializable_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { createTestBlock, defineRowBlock, diff --git a/tests/mocha/field_label_test.js b/tests/mocha/field_label_test.js index cf5b4904493..bae600aff19 100644 --- a/tests/mocha/field_label_test.js +++ b/tests/mocha/field_label_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {createTestBlock} from './test_helpers/block_definitions.js'; import { assertFieldValue, diff --git a/tests/mocha/field_number_test.js b/tests/mocha/field_number_test.js index 768766bf013..3c12fed820d 100644 --- a/tests/mocha/field_number_test.js +++ b/tests/mocha/field_number_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineRowBlock} from './test_helpers/block_definitions.js'; import {runTestCases} from './test_helpers/common.js'; import { diff --git a/tests/mocha/field_registry_test.js b/tests/mocha/field_registry_test.js index 26b33c16c3d..1f19477dee1 100644 --- a/tests/mocha/field_registry_test.js +++ b/tests/mocha/field_registry_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/field_test.js b/tests/mocha/field_test.js index 38f9662d6d6..422b0473418 100644 --- a/tests/mocha/field_test.js +++ b/tests/mocha/field_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { addBlockTypeToCleanup, addMessageToCleanup, diff --git a/tests/mocha/field_textinput_test.js b/tests/mocha/field_textinput_test.js index 7dc105f72f0..7cafd00d948 100644 --- a/tests/mocha/field_textinput_test.js +++ b/tests/mocha/field_textinput_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { createTestBlock, defineRowBlock, diff --git a/tests/mocha/field_variable_test.js b/tests/mocha/field_variable_test.js index 2dc8d35a55c..58a20977521 100644 --- a/tests/mocha/field_variable_test.js +++ b/tests/mocha/field_variable_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { createTestBlock, defineRowBlock, diff --git a/tests/mocha/flyout_test.js b/tests/mocha/flyout_test.js index f6d3019df55..998279a0874 100644 --- a/tests/mocha/flyout_test.js +++ b/tests/mocha/flyout_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/focus_manager_test.js b/tests/mocha/focus_manager_test.js index 26dcb8dbe68..490fa4301b8 100644 --- a/tests/mocha/focus_manager_test.js +++ b/tests/mocha/focus_manager_test.js @@ -8,7 +8,7 @@ import { FocusManager, getFocusManager, } from '../../build/src/core/focus_manager.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/focusable_tree_traverser_test.js b/tests/mocha/focusable_tree_traverser_test.js index 0f88e1106f9..a384dd4be45 100644 --- a/tests/mocha/focusable_tree_traverser_test.js +++ b/tests/mocha/focusable_tree_traverser_test.js @@ -6,7 +6,7 @@ import {FocusManager} from '../../build/src/core/focus_manager.js'; import {FocusableTreeTraverser} from '../../build/src/core/utils/focusable_tree_traverser.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/generator_test.js b/tests/mocha/generator_test.js index 527448eacc9..3c377e7c1ab 100644 --- a/tests/mocha/generator_test.js +++ b/tests/mocha/generator_test.js @@ -10,7 +10,7 @@ import {JavascriptGenerator} from '../../build/src/generators/javascript/javascr import {LuaGenerator} from '../../build/src/generators/lua/lua_generator.js'; import {PhpGenerator} from '../../build/src/generators/php/php_generator.js'; import {PythonGenerator} from '../../build/src/generators/python/python_generator.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/gesture_test.js b/tests/mocha/gesture_test.js index 3f53b8894b9..af4c599fea3 100644 --- a/tests/mocha/gesture_test.js +++ b/tests/mocha/gesture_test.js @@ -5,7 +5,7 @@ */ import {EventType} from '../../build/src/core/events/type.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineBasicBlockWithField} from './test_helpers/block_definitions.js'; import {assertEventFired, assertEventNotFired} from './test_helpers/events.js'; import { diff --git a/tests/mocha/icon_test.js b/tests/mocha/icon_test.js index 5855fcfc576..ba1b7116065 100644 --- a/tests/mocha/icon_test.js +++ b/tests/mocha/icon_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineEmptyBlock} from './test_helpers/block_definitions.js'; import {MockIcon, MockSerializableIcon} from './test_helpers/icon_mocks.js'; import { diff --git a/tests/mocha/input_test.js b/tests/mocha/input_test.js index 0c2b0973eaf..dfa30858e0e 100644 --- a/tests/mocha/input_test.js +++ b/tests/mocha/input_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/insertion_marker_test.js b/tests/mocha/insertion_marker_test.js index 9ccf8a00b0f..f8215a847eb 100644 --- a/tests/mocha/insertion_marker_test.js +++ b/tests/mocha/insertion_marker_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/jso_deserialization_test.js b/tests/mocha/jso_deserialization_test.js index dfd3e62b7f2..f6b47d7de6a 100644 --- a/tests/mocha/jso_deserialization_test.js +++ b/tests/mocha/jso_deserialization_test.js @@ -5,7 +5,7 @@ */ import {EventType} from '../../build/src/core/events/type.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {assertEventFired} from './test_helpers/events.js'; import { MockParameterModel, diff --git a/tests/mocha/jso_serialization_test.js b/tests/mocha/jso_serialization_test.js index 7cf415e676a..4a7d5e9e1d3 100644 --- a/tests/mocha/jso_serialization_test.js +++ b/tests/mocha/jso_serialization_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { defineRowBlock, defineStackBlock, diff --git a/tests/mocha/json_test.js b/tests/mocha/json_test.js index 471d2fb9711..2e4b68df9ec 100644 --- a/tests/mocha/json_test.js +++ b/tests/mocha/json_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { addMessageToCleanup, sharedTestSetup, diff --git a/tests/mocha/keyboard_navigation_controller_test.js b/tests/mocha/keyboard_navigation_controller_test.js index c7abd863ec1..dd81e9e4b45 100644 --- a/tests/mocha/keyboard_navigation_controller_test.js +++ b/tests/mocha/keyboard_navigation_controller_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/layering_test.js b/tests/mocha/layering_test.js index 1ef0ee6973d..b84f23252b3 100644 --- a/tests/mocha/layering_test.js +++ b/tests/mocha/layering_test.js @@ -3,7 +3,7 @@ * Copyright 2023 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/metrics_test.js b/tests/mocha/metrics_test.js index 860e802550d..de9a5641ad7 100644 --- a/tests/mocha/metrics_test.js +++ b/tests/mocha/metrics_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/mutator_test.js b/tests/mocha/mutator_test.js index fb6d8caf09b..72b17d0a4bb 100644 --- a/tests/mocha/mutator_test.js +++ b/tests/mocha/mutator_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { createRenderedBlock, defineMutatorBlocks, diff --git a/tests/mocha/names_test.js b/tests/mocha/names_test.js index 732e28cd57a..e449a59fd6c 100644 --- a/tests/mocha/names_test.js +++ b/tests/mocha/names_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/navigation_test.js b/tests/mocha/navigation_test.js index 5bed2aaab8c..38dc88894b1 100644 --- a/tests/mocha/navigation_test.js +++ b/tests/mocha/navigation_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/old_workspace_comment_test.js b/tests/mocha/old_workspace_comment_test.js index 08a2523f50e..5038d67bad8 100644 --- a/tests/mocha/old_workspace_comment_test.js +++ b/tests/mocha/old_workspace_comment_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/procedure_map_test.js b/tests/mocha/procedure_map_test.js index eebd5a9f326..5e29c6ca050 100644 --- a/tests/mocha/procedure_map_test.js +++ b/tests/mocha/procedure_map_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {MockProcedureModel} from './test_helpers/procedures.js'; import { sharedTestSetup, diff --git a/tests/mocha/rect_test.js b/tests/mocha/rect_test.js index 37712dff3a0..652837eaef6 100644 --- a/tests/mocha/rect_test.js +++ b/tests/mocha/rect_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/registry_test.js b/tests/mocha/registry_test.js index 6bcb8b5b077..2d5e22543ca 100644 --- a/tests/mocha/registry_test.js +++ b/tests/mocha/registry_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/render_management_test.js b/tests/mocha/render_management_test.js index 4de0635394b..94fa48805cf 100644 --- a/tests/mocha/render_management_test.js +++ b/tests/mocha/render_management_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/serializer_test.js b/tests/mocha/serializer_test.js index a3a3761e9c6..efd1f308b01 100644 --- a/tests/mocha/serializer_test.js +++ b/tests/mocha/serializer_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { TestCase, TestSuite, diff --git a/tests/mocha/shortcut_items_test.js b/tests/mocha/shortcut_items_test.js index d96ddbfeadc..dfbae3f0901 100644 --- a/tests/mocha/shortcut_items_test.js +++ b/tests/mocha/shortcut_items_test.js @@ -5,7 +5,7 @@ */ import * as Blockly from '../../build/src/core/blockly.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineStackBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/shortcut_registry_test.js b/tests/mocha/shortcut_registry_test.js index 5641e17c7d1..a06f01b9c00 100644 --- a/tests/mocha/shortcut_registry_test.js +++ b/tests/mocha/shortcut_registry_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {createTestBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/test_helpers/code_generation.js b/tests/mocha/test_helpers/code_generation.js index 95bd902cd45..e61a45653b2 100644 --- a/tests/mocha/test_helpers/code_generation.js +++ b/tests/mocha/test_helpers/code_generation.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; import {runTestSuites} from './common.js'; /** diff --git a/tests/mocha/test_helpers/events.js b/tests/mocha/test_helpers/events.js index c074bdd77a4..3e0b1e95d8b 100644 --- a/tests/mocha/test_helpers/events.js +++ b/tests/mocha/test_helpers/events.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; /** * Creates spy for workspace fireChangeListener diff --git a/tests/mocha/test_helpers/fields.js b/tests/mocha/test_helpers/fields.js index e082abb4ccc..ab304a808a7 100644 --- a/tests/mocha/test_helpers/fields.js +++ b/tests/mocha/test_helpers/fields.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; import {runTestCases, TestCase} from './common.js'; /** diff --git a/tests/mocha/test_helpers/procedures.js b/tests/mocha/test_helpers/procedures.js index ecf8c13adcc..16ef973350f 100644 --- a/tests/mocha/test_helpers/procedures.js +++ b/tests/mocha/test_helpers/procedures.js @@ -6,7 +6,7 @@ import {ConnectionType} from '../../../build/src/core/connection_type.js'; import {VariableModel} from '../../../build/src/core/variable_model.js'; -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; /** * Asserts that the procedure definition or call block has the expected var diff --git a/tests/mocha/test_helpers/serialization.js b/tests/mocha/test_helpers/serialization.js index c99f508d4dc..c476eae3d6f 100644 --- a/tests/mocha/test_helpers/serialization.js +++ b/tests/mocha/test_helpers/serialization.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; import {runTestCases} from './common.js'; /** diff --git a/tests/mocha/test_helpers/variables.js b/tests/mocha/test_helpers/variables.js index 83f175f6327..dd19b4904c9 100644 --- a/tests/mocha/test_helpers/variables.js +++ b/tests/mocha/test_helpers/variables.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; /** * Check if a variable with the given values exists. diff --git a/tests/mocha/test_helpers/warnings.js b/tests/mocha/test_helpers/warnings.js index 0e07f846c5a..d718a25c166 100644 --- a/tests/mocha/test_helpers/warnings.js +++ b/tests/mocha/test_helpers/warnings.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; /** * Captures the strings sent to console.warn() when calling a function. diff --git a/tests/mocha/test_helpers/workspace.js b/tests/mocha/test_helpers/workspace.js index 917ce6f629e..b5f3cadabd3 100644 --- a/tests/mocha/test_helpers/workspace.js +++ b/tests/mocha/test_helpers/workspace.js @@ -5,7 +5,7 @@ */ import * as eventUtils from '../../../build/src/core/events/utils.js'; -import {assert} from '../../../node_modules/chai/chai.js'; +import {assert} from '../../../node_modules/chai/index.js'; import {workspaceTeardown} from './setup_teardown.js'; import {assertVariableValues} from './variables.js'; import {assertWarnings} from './warnings.js'; diff --git a/tests/mocha/theme_test.js b/tests/mocha/theme_test.js index 1f425dca6a3..f54641a348f 100644 --- a/tests/mocha/theme_test.js +++ b/tests/mocha/theme_test.js @@ -5,7 +5,7 @@ */ import {EventType} from '../../build/src/core/events/type.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {assertEventFired} from './test_helpers/events.js'; import { sharedTestSetup, diff --git a/tests/mocha/toast_test.js b/tests/mocha/toast_test.js index 45e02ad5de8..afb7f7f6cb9 100644 --- a/tests/mocha/toast_test.js +++ b/tests/mocha/toast_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/toolbox_test.js b/tests/mocha/toolbox_test.js index 4e92cd28fd3..480fdfdc6fc 100644 --- a/tests/mocha/toolbox_test.js +++ b/tests/mocha/toolbox_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineStackBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/tooltip_test.js b/tests/mocha/tooltip_test.js index 1edc8ad6e25..0695b9ebe03 100644 --- a/tests/mocha/tooltip_test.js +++ b/tests/mocha/tooltip_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/touch_test.js b/tests/mocha/touch_test.js index 775665643b7..30a9fe72724 100644 --- a/tests/mocha/touch_test.js +++ b/tests/mocha/touch_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/trashcan_test.js b/tests/mocha/trashcan_test.js index 5486326f1e0..d96e00f3a21 100644 --- a/tests/mocha/trashcan_test.js +++ b/tests/mocha/trashcan_test.js @@ -6,7 +6,7 @@ import {EventType} from '../../build/src/core/events/type.js'; import * as eventUtils from '../../build/src/core/events/utils.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { defineBasicBlockWithField, defineMutatorBlocks, diff --git a/tests/mocha/utils_test.js b/tests/mocha/utils_test.js index b6228448db7..accf164b79b 100644 --- a/tests/mocha/utils_test.js +++ b/tests/mocha/utils_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/variable_map_test.js b/tests/mocha/variable_map_test.js index 2d6cee0b94a..76dffbe9dc7 100644 --- a/tests/mocha/variable_map_test.js +++ b/tests/mocha/variable_map_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { assertEventFired, assertEventNotFired, diff --git a/tests/mocha/variable_model_test.js b/tests/mocha/variable_model_test.js index cd2a89db420..eee7ea9bf41 100644 --- a/tests/mocha/variable_model_test.js +++ b/tests/mocha/variable_model_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/webdriver.js b/tests/mocha/webdriver.js index 207917c5e6b..06e7a3e6585 100644 --- a/tests/mocha/webdriver.js +++ b/tests/mocha/webdriver.js @@ -15,9 +15,12 @@ const {posixPath} = require('../../scripts/helpers'); * Runs the Mocha tests in this directory in Chrome. It uses webdriverio to * launch Chrome and load index.html. Outputs a summary of the test results * to the console. + * + * @param {boolean} exitOnCompletetion True if the browser should automatically + * quit after tests have finished running. * @return {number} 0 on success, 1 on failure. */ -async function runMochaTestsInBrowser() { +async function runMochaTestsInBrowser(exitOnCompletion = true) { const options = { capabilities: { browserName: 'chrome', @@ -45,6 +48,17 @@ async function runMochaTestsInBrowser() { console.log('Loading URL: ' + url); await browser.url(url); + // Toggle the devtools setting to emulate focus, so that the window will + // always act as if it has focus regardless of the state of the window manager + // or operating system. This improves the reliability of FocusManager-related + // tests. + const puppeteer = await browser.getPuppeteer(); + await browser.call(async () => { + const page = (await puppeteer.pages())[0]; + const session = await page.createCDPSession(); + await session.send('Emulation.setFocusEmulationEnabled', { enabled: true }); + }); + await browser.waitUntil(async() => { const elem = await browser.$('#failureCount'); const text = await elem.getAttribute('tests_failed'); @@ -74,7 +88,7 @@ async function runMochaTestsInBrowser() { if (parseInt(numOfFailure) !== 0) { return 1; } - await browser.deleteSession(); + if (exitOnCompletion) await browser.deleteSession(); return 0; } diff --git a/tests/mocha/widget_div_test.js b/tests/mocha/widget_div_test.js index 61c94247110..4ad31f96c72 100644 --- a/tests/mocha/widget_div_test.js +++ b/tests/mocha/widget_div_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/workspace_comment_test.js b/tests/mocha/workspace_comment_test.js index 3ce276e8579..bb87ad82ac6 100644 --- a/tests/mocha/workspace_comment_test.js +++ b/tests/mocha/workspace_comment_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { assertEventFired, createChangeListenerSpy, diff --git a/tests/mocha/workspace_svg_test.js b/tests/mocha/workspace_svg_test.js index 207cad45dc7..6193dda2db7 100644 --- a/tests/mocha/workspace_svg_test.js +++ b/tests/mocha/workspace_svg_test.js @@ -5,7 +5,7 @@ */ import {EventType} from '../../build/src/core/events/type.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {defineStackBlock} from './test_helpers/block_definitions.js'; import { assertEventFired, diff --git a/tests/mocha/xml_test.js b/tests/mocha/xml_test.js index 218324197bf..cd1bce128bb 100644 --- a/tests/mocha/xml_test.js +++ b/tests/mocha/xml_test.js @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import { addBlockTypeToCleanup, createGenUidStubWithReturns, diff --git a/tests/mocha/zoom_controls_test.js b/tests/mocha/zoom_controls_test.js index dedc36b75b4..d9bb0f91e9b 100644 --- a/tests/mocha/zoom_controls_test.js +++ b/tests/mocha/zoom_controls_test.js @@ -5,7 +5,7 @@ */ import {EventType} from '../../build/src/core/events/type.js'; -import {assert} from '../../node_modules/chai/chai.js'; +import {assert} from '../../node_modules/chai/index.js'; import {assertEventFired, assertEventNotFired} from './test_helpers/events.js'; import { sharedTestSetup,