mirror of
https://github.com/google/blockly.git
synced 2026-01-04 15:40:08 +01:00
fix: creating duplicate params via renaming vars (#6885)
* feat: add IVariableHolder * chore: reorganize variable prompts to early return * fix: add retriggering prompt for conflicting params * chore: add unit tests * chore: fix build * chore: reorganize checking for param conflicts * fix: visibility * chore: rename variable holder interface * chore: fix typo * chore: fix lint
This commit is contained in:
@@ -176,6 +176,7 @@ function buildTSOverride({files, tsconfig}) {
|
|||||||
'jsdoc/check-param-names': ['off', {'checkDestructured': false}],
|
'jsdoc/check-param-names': ['off', {'checkDestructured': false}],
|
||||||
// Allow any text in the license tag. Other checks are not relevant.
|
// Allow any text in the license tag. Other checks are not relevant.
|
||||||
'jsdoc/check-values': ['off'],
|
'jsdoc/check-values': ['off'],
|
||||||
|
'jsdoc/newline-after-description': ['error'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ import {ISelectableToolboxItem} from './interfaces/i_selectable_toolbox_item.js'
|
|||||||
import {IStyleable} from './interfaces/i_styleable.js';
|
import {IStyleable} from './interfaces/i_styleable.js';
|
||||||
import {IToolbox} from './interfaces/i_toolbox.js';
|
import {IToolbox} from './interfaces/i_toolbox.js';
|
||||||
import {IToolboxItem} from './interfaces/i_toolbox_item.js';
|
import {IToolboxItem} from './interfaces/i_toolbox_item.js';
|
||||||
|
import {IVariableBackedParameterModel, isVariableBackedParameterModel} from './interfaces/i_variable_backed_parameter_model.js';
|
||||||
import * as internalConstants from './internal_constants.js';
|
import * as internalConstants from './internal_constants.js';
|
||||||
import {ASTNode} from './keyboard_nav/ast_node.js';
|
import {ASTNode} from './keyboard_nav/ast_node.js';
|
||||||
import {BasicCursor} from './keyboard_nav/basic_cursor.js';
|
import {BasicCursor} from './keyboard_nav/basic_cursor.js';
|
||||||
@@ -701,6 +702,7 @@ export {ISelectableToolboxItem};
|
|||||||
export {IStyleable};
|
export {IStyleable};
|
||||||
export {IToolbox};
|
export {IToolbox};
|
||||||
export {IToolboxItem};
|
export {IToolboxItem};
|
||||||
|
export {IVariableBackedParameterModel, isVariableBackedParameterModel};
|
||||||
export {Marker};
|
export {Marker};
|
||||||
export {MarkerManager};
|
export {MarkerManager};
|
||||||
export {Menu};
|
export {Menu};
|
||||||
|
|||||||
47
core/interfaces/i_legacy_procedure_blocks.ts
Normal file
47
core/interfaces/i_legacy_procedure_blocks.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2023 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy means of representing a procedure signature. The elements are
|
||||||
|
* respectively: name, parameter names, and whether it has a return value.
|
||||||
|
*/
|
||||||
|
export type ProcedureTuple = [string, string[], boolean];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Procedure block type.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export interface ProcedureBlock {
|
||||||
|
getProcedureCall: () => string;
|
||||||
|
renameProcedure: (p1: string, p2: string) => void;
|
||||||
|
getProcedureDef: () => ProcedureTuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export interface LegacyProcedureDefBlock {
|
||||||
|
getProcedureDef: () => ProcedureTuple
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function isLegacyProcedureDefBlock(block: Object):
|
||||||
|
block is LegacyProcedureDefBlock {
|
||||||
|
return (block as any).getProcedureDef !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export interface LegacyProcedureCallBlock {
|
||||||
|
getProcedureCall: () => string;
|
||||||
|
renameProcedure: (p1: string, p2: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function isLegacyProcedureCallBlock(block: Object):
|
||||||
|
block is LegacyProcedureCallBlock {
|
||||||
|
return (block as any).getProcedureCall !== undefined &&
|
||||||
|
(block as any).renameProcedure !== undefined;
|
||||||
|
}
|
||||||
23
core/interfaces/i_variable_backed_parameter_model.ts
Normal file
23
core/interfaces/i_variable_backed_parameter_model.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2023 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {VariableModel} from '../variable_model.js';
|
||||||
|
import {IParameterModel} from './i_parameter_model.js';
|
||||||
|
|
||||||
|
|
||||||
|
/** Interface for a parameter model that holds a variable model. */
|
||||||
|
export interface IVariableBackedParameterModel extends IParameterModel {
|
||||||
|
/** Returns the variable model held by this type. */
|
||||||
|
getVariableModel(): VariableModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given object is a variable holder or not.
|
||||||
|
*/
|
||||||
|
export function isVariableBackedParameterModel(param: IParameterModel):
|
||||||
|
param is IVariableBackedParameterModel {
|
||||||
|
return (param as any).getVariableModel !== undefined;
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import {IParameterModel} from './interfaces/i_parameter_model.js';
|
|||||||
import {IProcedureMap} from './interfaces/i_procedure_map.js';
|
import {IProcedureMap} from './interfaces/i_procedure_map.js';
|
||||||
import {IProcedureModel} from './interfaces/i_procedure_model.js';
|
import {IProcedureModel} from './interfaces/i_procedure_model.js';
|
||||||
import {IProcedureBlock, isProcedureBlock} from './interfaces/i_procedure_block.js';
|
import {IProcedureBlock, isProcedureBlock} from './interfaces/i_procedure_block.js';
|
||||||
|
import {isLegacyProcedureCallBlock, isLegacyProcedureDefBlock, ProcedureBlock, ProcedureTuple} from './interfaces/i_legacy_procedure_blocks.js';
|
||||||
import {ObservableProcedureMap} from './observable_procedure_map.js';
|
import {ObservableProcedureMap} from './observable_procedure_map.js';
|
||||||
import * as utilsXml from './utils/xml.js';
|
import * as utilsXml from './utils/xml.js';
|
||||||
import * as Variables from './variables.js';
|
import * as Variables from './variables.js';
|
||||||
@@ -50,37 +51,6 @@ export const CATEGORY_NAME = 'PROCEDURE';
|
|||||||
*/
|
*/
|
||||||
export const DEFAULT_ARG = 'x';
|
export const DEFAULT_ARG = 'x';
|
||||||
|
|
||||||
export type ProcedureTuple = [string, string[], boolean];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Procedure block type.
|
|
||||||
*/
|
|
||||||
export interface ProcedureBlock {
|
|
||||||
getProcedureCall: () => string;
|
|
||||||
renameProcedure: (p1: string, p2: string) => void;
|
|
||||||
getProcedureDef: () => ProcedureTuple;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LegacyProcedureDefBlock {
|
|
||||||
getProcedureDef: () => ProcedureTuple
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLegacyProcedureDefBlock(block: Object):
|
|
||||||
block is LegacyProcedureDefBlock {
|
|
||||||
return (block as any).getProcedureDef !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LegacyProcedureCallBlock {
|
|
||||||
getProcedureCall: () => string;
|
|
||||||
renameProcedure: (p1: string, p2: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLegacyProcedureCallBlock(block: Object):
|
|
||||||
block is LegacyProcedureCallBlock {
|
|
||||||
return (block as any).getProcedureCall !== undefined &&
|
|
||||||
(block as any).renameProcedure !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all user-created procedure definitions in a workspace.
|
* Find all user-created procedure definitions in a workspace.
|
||||||
*
|
*
|
||||||
@@ -492,4 +462,5 @@ export {
|
|||||||
isProcedureBlock,
|
isProcedureBlock,
|
||||||
IProcedureMap,
|
IProcedureMap,
|
||||||
IProcedureModel,
|
IProcedureModel,
|
||||||
|
ProcedureTuple,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ goog.declareModuleId('Blockly.Variables');
|
|||||||
|
|
||||||
import {Blocks} from './blocks.js';
|
import {Blocks} from './blocks.js';
|
||||||
import * as dialog from './dialog.js';
|
import * as dialog from './dialog.js';
|
||||||
|
import {isVariableBackedParameterModel} from './interfaces/i_variable_backed_parameter_model.js';
|
||||||
import {Msg} from './msg.js';
|
import {Msg} from './msg.js';
|
||||||
|
import {isLegacyProcedureDefBlock} from './interfaces/i_legacy_procedure_blocks.js';
|
||||||
import * as utilsXml from './utils/xml.js';
|
import * as utilsXml from './utils/xml.js';
|
||||||
import {VariableModel} from './variable_model.js';
|
import {VariableModel} from './variable_model.js';
|
||||||
import type {Workspace} from './workspace.js';
|
import type {Workspace} from './workspace.js';
|
||||||
@@ -249,32 +251,28 @@ export function createVariableButtonHandler(
|
|||||||
// This function needs to be named so it can be called recursively.
|
// This function needs to be named so it can be called recursively.
|
||||||
function promptAndCheckWithAlert(defaultName: string) {
|
function promptAndCheckWithAlert(defaultName: string) {
|
||||||
promptName(Msg['NEW_VARIABLE_TITLE'], defaultName, function(text) {
|
promptName(Msg['NEW_VARIABLE_TITLE'], defaultName, function(text) {
|
||||||
if (text) {
|
if (!text) { // User canceled prompt.
|
||||||
const existing = nameUsedWithAnyType(text, workspace);
|
if (opt_callback) opt_callback(null);
|
||||||
if (existing) {
|
return;
|
||||||
let msg;
|
|
||||||
if (existing.type === type) {
|
|
||||||
msg = Msg['VARIABLE_ALREADY_EXISTS'].replace('%1', existing.name);
|
|
||||||
} else {
|
|
||||||
msg = Msg['VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE'];
|
|
||||||
msg = msg.replace('%1', existing.name).replace('%2', existing.type);
|
|
||||||
}
|
|
||||||
dialog.alert(msg, function() {
|
|
||||||
promptAndCheckWithAlert(text);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// No conflict
|
|
||||||
workspace.createVariable(text, type);
|
|
||||||
if (opt_callback) {
|
|
||||||
opt_callback(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// User canceled prompt.
|
|
||||||
if (opt_callback) {
|
|
||||||
opt_callback(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existing = nameUsedWithAnyType(text, workspace);
|
||||||
|
if (!existing) { // No conflict
|
||||||
|
workspace.createVariable(text, type);
|
||||||
|
if (opt_callback) opt_callback(text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg;
|
||||||
|
if (existing.type === type) {
|
||||||
|
msg = Msg['VARIABLE_ALREADY_EXISTS'].replace('%1', existing.name);
|
||||||
|
} else {
|
||||||
|
msg = Msg['VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE'];
|
||||||
|
msg = msg.replace('%1', existing.name).replace('%2', existing.type);
|
||||||
|
}
|
||||||
|
dialog.alert(msg, function() {
|
||||||
|
promptAndCheckWithAlert(text);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
promptAndCheckWithAlert('');
|
promptAndCheckWithAlert('');
|
||||||
@@ -299,28 +297,33 @@ export function renameVariable(
|
|||||||
const promptText =
|
const promptText =
|
||||||
Msg['RENAME_VARIABLE_TITLE'].replace('%1', variable.name);
|
Msg['RENAME_VARIABLE_TITLE'].replace('%1', variable.name);
|
||||||
promptName(promptText, defaultName, function(newName) {
|
promptName(promptText, defaultName, function(newName) {
|
||||||
if (newName) {
|
if (!newName) { // User canceled prompt.
|
||||||
const existing =
|
if (opt_callback) opt_callback(null);
|
||||||
nameUsedWithOtherType(newName, variable.type, workspace);
|
return;
|
||||||
if (existing) {
|
|
||||||
const msg = Msg['VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE']
|
|
||||||
.replace('%1', existing.name)
|
|
||||||
.replace('%2', existing.type);
|
|
||||||
dialog.alert(msg, function() {
|
|
||||||
promptAndCheckWithAlert(newName);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
workspace.renameVariableById(variable.getId(), newName);
|
|
||||||
if (opt_callback) {
|
|
||||||
opt_callback(newName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// User canceled prompt.
|
|
||||||
if (opt_callback) {
|
|
||||||
opt_callback(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existing = nameUsedWithOtherType(newName, variable.type, workspace);
|
||||||
|
const procedure =
|
||||||
|
nameUsedWithConflictingParam(variable.name, newName, workspace);
|
||||||
|
if (!existing && !procedure) { // No conflict.
|
||||||
|
workspace.renameVariableById(variable.getId(), newName);
|
||||||
|
if (opt_callback) opt_callback(newName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = '';
|
||||||
|
if (existing) {
|
||||||
|
msg = Msg['VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE']
|
||||||
|
.replace('%1', existing.name)
|
||||||
|
.replace('%2', existing.type);
|
||||||
|
} else if (procedure) {
|
||||||
|
msg = Msg['VARIABLE_ALREADY_EXISTS_FOR_A_PARAMETER']
|
||||||
|
.replace('%1', newName)
|
||||||
|
.replace('%2', procedure);
|
||||||
|
}
|
||||||
|
dialog.alert(msg, function() {
|
||||||
|
promptAndCheckWithAlert(newName);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
promptAndCheckWithAlert('');
|
promptAndCheckWithAlert('');
|
||||||
@@ -393,6 +396,68 @@ export function nameUsedWithAnyType(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the procedure with a conflicting parameter name, or null
|
||||||
|
* if one does not exist.
|
||||||
|
*
|
||||||
|
* This checks the procedure map if it contains models, and the legacy procedure
|
||||||
|
* blocks otherwise.
|
||||||
|
*
|
||||||
|
* @param oldName The old name of the variable.
|
||||||
|
* @param newName The proposed name of the variable.
|
||||||
|
* @param workspace The workspace to search for conflicting parameters.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function nameUsedWithConflictingParam(
|
||||||
|
oldName: string, newName: string, workspace: Workspace): string|null {
|
||||||
|
return workspace.getProcedureMap().getProcedures().length ?
|
||||||
|
checkForConflictingParamWithProcedureModels(oldName, newName, workspace) :
|
||||||
|
checkForConflictingParamWithLegacyProcedures(oldName, newName, workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the procedure model with a conflicting param name, or
|
||||||
|
* null if one does not exist.
|
||||||
|
*/
|
||||||
|
function checkForConflictingParamWithProcedureModels(
|
||||||
|
oldName: string, newName: string, workspace: Workspace): string|null {
|
||||||
|
oldName = oldName.toLowerCase();
|
||||||
|
newName = newName.toLowerCase();
|
||||||
|
|
||||||
|
const procedures = workspace.getProcedureMap().getProcedures();
|
||||||
|
for (const procedure of procedures) {
|
||||||
|
const params = procedure.getParameters()
|
||||||
|
.filter(isVariableBackedParameterModel)
|
||||||
|
.map((param) => param.getVariableModel().name);
|
||||||
|
if (!params) continue;
|
||||||
|
const procHasOld = params.some((param) => param.toLowerCase() === oldName);
|
||||||
|
const procHasNew = params.some((param) => param.toLowerCase() === newName);
|
||||||
|
if (procHasOld && procHasNew) return procedure.getName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the procedure block with a conflicting param name, or
|
||||||
|
* null if one does not exist.
|
||||||
|
*/
|
||||||
|
function checkForConflictingParamWithLegacyProcedures(
|
||||||
|
oldName: string, newName: string, workspace: Workspace): string|null {
|
||||||
|
oldName = oldName.toLowerCase();
|
||||||
|
newName = newName.toLowerCase();
|
||||||
|
|
||||||
|
const blocks = workspace.getAllBlocks(false);
|
||||||
|
for (const block of blocks) {
|
||||||
|
if (!isLegacyProcedureDefBlock(block)) continue;
|
||||||
|
const def = block.getProcedureDef();
|
||||||
|
const params = def[1];
|
||||||
|
const blockHasOld = params.some((param) => param.toLowerCase() === oldName);
|
||||||
|
const blockHasNew = params.some((param) => param.toLowerCase() === newName);
|
||||||
|
if (blockHasOld && blockHasNew) return def[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate DOM objects representing a variable field.
|
* Generate DOM objects representing a variable field.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -166,6 +166,9 @@ Blockly.Msg.VARIABLE_ALREADY_EXISTS = 'A variable named "%1" already exists.';
|
|||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
/// alert - Tells the user that the name they entered is already in use for another type.
|
/// alert - Tells the user that the name they entered is already in use for another type.
|
||||||
Blockly.Msg.VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE = 'A variable named "%1" already exists for another type: "%2".';
|
Blockly.Msg.VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE = 'A variable named "%1" already exists for another type: "%2".';
|
||||||
|
/** @type {string} */
|
||||||
|
/// alert - Tells the user that the name they entered is already in use as a parameter to a procedure, that the variable they are renaming also exists on. Renaming would create two parameters with the same name, which is not allowed.
|
||||||
|
Blockly.Msg.VARIABLE_ALREADY_EXISTS_FOR_A_PARAMETER = 'A variable named "%1" already exists as a parameter in the procedure "%2".';
|
||||||
|
|
||||||
// Variable deletion.
|
// Variable deletion.
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
goog.declareModuleId('Blockly.test.variables');
|
goog.declareModuleId('Blockly.test.variables');
|
||||||
|
|
||||||
import {sharedTestSetup, sharedTestTeardown} from '../test_helpers/setup_teardown.js';
|
import {sharedTestSetup, sharedTestTeardown} from '../test_helpers/setup_teardown.js';
|
||||||
|
import {nameUsedWithConflictingParam} from '../../../build/src/core/variables.js';
|
||||||
|
import {MockParameterModelWithVar, MockProcedureModel} from '../test_helpers/procedures.js';
|
||||||
|
|
||||||
|
|
||||||
suite('Variables', function() {
|
suite('Variables', function() {
|
||||||
@@ -139,4 +141,100 @@ suite('Variables', function() {
|
|||||||
chai.assert.equal(var3, result3);
|
chai.assert.equal(var3, result3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
suite('renaming variables creating conflicts', function() {
|
||||||
|
suite('renaming variables creating parameter conflicts', function() {
|
||||||
|
test(
|
||||||
|
'conflicts within legacy procedure blocks return the procedure name',
|
||||||
|
function() {
|
||||||
|
Blockly.serialization.blocks.append({
|
||||||
|
'type': 'procedures_defnoreturn',
|
||||||
|
'extraState': {
|
||||||
|
'params': [
|
||||||
|
{
|
||||||
|
'name': 'x',
|
||||||
|
'id': '6l3P%Y!9EgA(Nh{E`Tl,',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'y',
|
||||||
|
'id': 'l1EtlJe%z_M[O-@uPAQ8',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'fields': {
|
||||||
|
'NAME': 'test name',
|
||||||
|
},
|
||||||
|
}, this.workspace);
|
||||||
|
|
||||||
|
chai.assert.equal(
|
||||||
|
'test name',
|
||||||
|
nameUsedWithConflictingParam('x', 'y', this.workspace),
|
||||||
|
'Expected the name of the procedure with the conflicting ' +
|
||||||
|
'param to be returned');
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'if no legacy block has the old var name, no procedure ' +
|
||||||
|
'name is returned',
|
||||||
|
function() {
|
||||||
|
Blockly.serialization.blocks.append({
|
||||||
|
'type': 'procedures_defnoreturn',
|
||||||
|
'extraState': {
|
||||||
|
'params': [
|
||||||
|
{
|
||||||
|
'name': 'definitely not x',
|
||||||
|
'id': '6l3P%Y!9EgA(Nh{E`Tl,',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'y',
|
||||||
|
'id': 'l1EtlJe%z_M[O-@uPAQ8',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'fields': {
|
||||||
|
'NAME': 'test name',
|
||||||
|
},
|
||||||
|
}, this.workspace);
|
||||||
|
|
||||||
|
chai.assert.isNull(
|
||||||
|
nameUsedWithConflictingParam('x', 'y', this.workspace),
|
||||||
|
'Expected there to be no conflict');
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'conflicts within procedure models return the procedure name',
|
||||||
|
function() {
|
||||||
|
this.workspace.getProcedureMap().add(
|
||||||
|
new MockProcedureModel('test name')
|
||||||
|
.insertParameter(
|
||||||
|
new MockParameterModelWithVar('x', this.workspace), 0)
|
||||||
|
.insertParameter(
|
||||||
|
new MockParameterModelWithVar('y', this.workspace), 0));
|
||||||
|
|
||||||
|
chai.assert.equal(
|
||||||
|
'test name',
|
||||||
|
nameUsedWithConflictingParam('x', 'y', this.workspace),
|
||||||
|
'Expected the name of the procedure with the conflicting ' +
|
||||||
|
'param to be returned');
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'if no procedure model has the old var, no procedure ' +
|
||||||
|
'name is returned',
|
||||||
|
function() {
|
||||||
|
this.workspace.getProcedureMap().add(
|
||||||
|
new MockProcedureModel('test name')
|
||||||
|
.insertParameter(
|
||||||
|
new MockParameterModelWithVar(
|
||||||
|
'definitely not x', this.workspace),
|
||||||
|
0)
|
||||||
|
.insertParameter(
|
||||||
|
new MockParameterModelWithVar('y', this.workspace), 0));
|
||||||
|
|
||||||
|
chai.assert.isNull(
|
||||||
|
nameUsedWithConflictingParam('x', 'y', this.workspace),
|
||||||
|
'Expected there to be no conflict');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
goog.declareModuleId('Blockly.test.helpers.procedures');
|
goog.declareModuleId('Blockly.test.helpers.procedures');
|
||||||
|
|
||||||
import {ConnectionType} from '../../../build/src/core/connection_type.js';
|
import {ConnectionType} from '../../../build/src/core/connection_type.js';
|
||||||
|
import {VariableModel} from '../../../build/src/core/variable_model.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,9 +151,9 @@ export function createProcCallBlock(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MockProcedureModel {
|
export class MockProcedureModel {
|
||||||
constructor() {
|
constructor(name = '') {
|
||||||
this.id = Blockly.utils.idGenerator.genUid();
|
this.id = Blockly.utils.idGenerator.genUid();
|
||||||
this.name = '';
|
this.name = name;
|
||||||
this.parameters = [];
|
this.parameters = [];
|
||||||
this.returnTypes = null;
|
this.returnTypes = null;
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
@@ -241,3 +242,14 @@ export class MockParameterModel {
|
|||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MockParameterModelWithVar extends MockParameterModel {
|
||||||
|
constructor(name, workspace) {
|
||||||
|
super(name);
|
||||||
|
this.variable = new VariableModel(workspace, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getVariableModel() {
|
||||||
|
return this.variable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user