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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/toolbox-search/src/block_searcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class BlockSearcher {
if (normalizedInput.length <= 3) return [normalizedInput];

const trigrams: string[] = [];
for (let start = 0; start < normalizedInput.length - 3; start++) {
for (let start = 0; start <= normalizedInput.length - 3; start++) {
trigrams.push(normalizedInput.substring(start, start + 3));
}

Expand Down
122 changes: 122 additions & 0 deletions plugins/toolbox-search/test/tests.mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ suite('Toolbox search', () => {
});

suite('BlockSearcher', () => {
test('generateTrigrams handles empty and short input', () => {
const searcher = new BlockSearcher();
const generateTrigrams = searcher.generateTrigrams.bind(searcher);

assert.deepEqual(generateTrigrams(''), []);
assert.deepEqual(generateTrigrams('a'), ['a']);
assert.deepEqual(generateTrigrams('abc'), ['abc']);
});

test('indexes the default value of dropdown fields', () => {
const searcher = new BlockSearcher();
const blocks = [
Expand Down Expand Up @@ -57,6 +66,119 @@ suite('BlockSearcher', () => {
assert.sameMembers(ransomNoteMatches, [listCreateWithBlock]);
});

test('requires the final trigram when matching longer queries', () => {
const searcher = new BlockSearcher();
const mathConstrainBlock = {
kind: 'block',
type: 'math_constrain',
};
searcher.indexBlocks([mathConstrainBlock]);

const matches = searcher.blockTypesMatching('conso');

assert.notInclude(
matches,
mathConstrainBlock,
'query missing trailing trigram should not match',
);
});

test('normalizes underscores in block types to spaces', () => {
if (!Blockly.Blocks['searcher_underscore_block']) {
Blockly.defineBlocksWithJsonArray([
{
type: 'searcher_underscore_block',
message0: 'custom block with underscore',
},
]);
}

const searcher = new BlockSearcher();
const blockInfo = {
kind: 'block',
type: 'searcher_underscore_block',
};
searcher.indexBlocks([blockInfo]);

assert.sameMembers(
searcher.blockTypesMatching('custom block with underscore'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the test is verifying that underscores in the block type get treated as spaces, should this be 'searcher underscore block'? As written this would match the unmodified message, but not verify the normalization of the block type.

[blockInfo],
);
assert.isEmpty(searcher.blockTypesMatching('custom_block_with_underscore'));
});

test('longer queries disambiguate similar blocks', () => {
if (!Blockly.Blocks['searcher_charlie']) {
Blockly.defineBlocksWithJsonArray([
{
type: 'searcher_charlie',
message0: 'alpha bravo charlie',
},
{
type: 'searcher_delta',
message0: 'alpha bravo delta',
},
]);
}

const searcher = new BlockSearcher();
const blockA = {kind: 'block', type: 'searcher_charlie'};
const blockB = {kind: 'block', type: 'searcher_delta'};

searcher.indexBlocks([blockA, blockB]);

const broadQueryMatches = searcher.blockTypesMatching('alpha bravo');
assert.sameMembers(broadQueryMatches, [blockA, blockB]);

const specificQueryMatches =
searcher.blockTypesMatching('alpha bravo charlie');
assert.sameMembers(specificQueryMatches, [blockA]);
});

test('indexes dropdown alt text options', () => {
if (!Blockly.Blocks['searcher_dropdown_alt']) {
Blockly.defineBlocksWithJsonArray([
{
type: 'searcher_dropdown_alt',
message0: 'weather %1',
args0: [
{
type: 'field_dropdown',
name: 'WEATHER',
options: [
[
{
src: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEA',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't appear to actually be a valid image; if it doesn't otherwise cause problems, can you set src to '' here and below to avoid confusion as to what this is and whether the particular value is relevant to the test case?

width: 1,
height: 1,
alt: 'Sunny',
},
'SUN',
],
[
{
src: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEA',
width: 1,
height: 1,
alt: 'Cloudy',
},
'CLOUD',
],
],
},
],
},
]);
}

const searcher = new BlockSearcher();
const blockInfo = {kind: 'block', type: 'searcher_dropdown_alt'};
searcher.indexBlocks([blockInfo]);

assert.sameMembers(searcher.blockTypesMatching('sunny'), [blockInfo]);
assert.sameMembers(searcher.blockTypesMatching('cloudy'), [blockInfo]);
});

test('returns an empty list when no matches are found', () => {
const searcher = new BlockSearcher();
assert.isEmpty(searcher.blockTypesMatching('abc123'));
Expand Down
Loading