go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/common/queries/tr_search_query.test.ts (about) 1 // Copyright 2021 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 import { 16 TestResult, 17 TestStatus, 18 TestVariant, 19 TestVariantStatus, 20 } from '@/common/services/resultdb'; 21 22 import { 23 parseTestResultSearchQuery, 24 suggestTestResultSearchQuery, 25 } from './tr_search_query'; 26 27 const variant1: TestVariant = { 28 testId: 'invocation-a/test-suite-a/test-1', 29 sourcesId: '1', 30 variant: { def: { key1: 'val1' } }, 31 variantHash: 'key1:val1', 32 testMetadata: { 33 name: 'test-name-1', 34 }, 35 status: TestVariantStatus.UNEXPECTED, 36 results: [ 37 { 38 result: { 39 status: TestStatus.Fail, 40 tags: [{ key: 'tag-key-1', value: 'tag-val-1' }], 41 duration: '10s', 42 } as TestResult, 43 }, 44 { 45 result: { 46 status: TestStatus.Fail, 47 tags: [{ key: 'tag-key-1', value: 'tag-val-1=1' }], 48 duration: '15s', 49 } as TestResult, 50 }, 51 { 52 result: { 53 status: TestStatus.Skip, 54 tags: [{ key: 'tag-key-2', value: 'tag-val-2' }], 55 duration: '20s', 56 } as TestResult, 57 }, 58 ], 59 }; 60 61 const variant2: TestVariant = { 62 testId: 'invocation-a/test-suite-a/test-2', 63 sourcesId: '1', 64 variant: { def: { key1: 'val2' } }, 65 variantHash: 'key1:val2', 66 testMetadata: { 67 name: 'test-name-2', 68 }, 69 status: TestVariantStatus.UNEXPECTED, 70 results: [ 71 { 72 result: { 73 tags: [{ key: 'tag-key-1', value: 'unknown-val' }], 74 status: TestStatus.Fail, 75 duration: '30s', 76 } as TestResult, 77 }, 78 { 79 result: { 80 status: TestStatus.Fail, 81 tags: [ 82 { key: 'duplicated-tag-key', value: 'first-tag-val' }, 83 { key: 'duplicated-tag-key', value: 'second-tag-val' }, 84 ], 85 } as TestResult, 86 }, 87 ], 88 }; 89 90 const variant3: TestVariant = { 91 testId: 'invocation-a/test-suite-b/test-3', 92 sourcesId: '1', 93 variant: { def: { key1: 'val3' } }, 94 variantHash: 'key1:val3', 95 testMetadata: { 96 name: 'test', 97 }, 98 status: TestVariantStatus.FLAKY, 99 results: [ 100 { 101 result: { 102 status: TestStatus.Pass, 103 } as TestResult, 104 }, 105 { 106 result: { 107 status: TestStatus.Fail, 108 } as TestResult, 109 }, 110 ], 111 }; 112 113 const variant4: TestVariant = { 114 testId: 'invocation-a/test-suite-B/test-4', 115 sourcesId: '1', 116 variant: { def: { key1: 'val2' } }, 117 variantHash: 'key1:val2', 118 status: TestVariantStatus.EXONERATED, 119 }; 120 121 const variant5: TestVariant = { 122 testId: 'invocation-a/test-suite-B/test-5', 123 sourcesId: '1', 124 variant: { def: { key1: 'val2', key2: 'val1' } }, 125 variantHash: 'key1:val2|key2:val1', 126 status: TestVariantStatus.EXPECTED, 127 results: [ 128 { 129 result: { 130 status: TestStatus.Pass, 131 } as TestResult, 132 }, 133 ], 134 }; 135 136 const variant6: TestVariant = { 137 testId: 'invocation-a/test-suite-b/test-5', 138 sourcesId: '1', 139 variant: { def: { key1: 'val2', key2: 'val3' } }, 140 variantHash: 'key1:val2|key2:val3', 141 testMetadata: { 142 name: 'sub', 143 }, 144 status: TestVariantStatus.EXPECTED, 145 results: [ 146 { 147 result: { 148 status: TestStatus.Skip, 149 } as TestResult, 150 }, 151 ], 152 }; 153 154 const variant7: TestVariant = { 155 testId: 'invocation-a/test-suite-b/test-5/sub', 156 sourcesId: '1', 157 variant: { def: { key1: 'val2', key2: 'val3=val' } }, 158 variantHash: 'key1:val2|key2:val3=val', 159 status: TestVariantStatus.EXPECTED, 160 results: [ 161 { 162 result: { 163 status: TestStatus.Skip, 164 } as TestResult, 165 }, 166 ], 167 }; 168 169 const variants = [ 170 variant1, 171 variant2, 172 variant3, 173 variant4, 174 variant5, 175 variant6, 176 variant7, 177 ]; 178 179 describe('parseTestResultSearchQuery', () => { 180 describe('query with no type', () => { 181 test('should match either test ID or test name', () => { 182 const filter = parseTestResultSearchQuery('sub'); 183 const filtered = variants.filter(filter); 184 expect(filtered).toEqual([variant6, variant7]); 185 }); 186 187 test('should be case insensitive', () => { 188 const filter = parseTestResultSearchQuery('SuB'); 189 const filtered = variants.filter(filter); 190 expect(filtered).toEqual([variant6, variant7]); 191 }); 192 }); 193 194 describe('ID query', () => { 195 test("should filter out variants whose test ID doesn't match the search text", () => { 196 const filter = parseTestResultSearchQuery('ID:test-suite-a'); 197 const filtered = variants.filter(filter); 198 expect(filtered).toEqual([variant1, variant2]); 199 }); 200 201 test('should be case insensitive', () => { 202 const filter = parseTestResultSearchQuery('id:test-suite-b'); 203 const filtered = variants.filter(filter); 204 expect(filtered).toEqual([ 205 variant3, 206 variant4, 207 variant5, 208 variant6, 209 variant7, 210 ]); 211 }); 212 213 test('should work with negation', () => { 214 const filter = parseTestResultSearchQuery('-id:test-5'); 215 const filtered = variants.filter(filter); 216 expect(filtered).toEqual([variant1, variant2, variant3, variant4]); 217 }); 218 }); 219 220 describe('RSTATUS query', () => { 221 test('should filter out variants with no matching status', () => { 222 const filter = parseTestResultSearchQuery('rstatus:pass'); 223 const filtered = variants.filter(filter); 224 expect(filtered).toEqual([variant3, variant5]); 225 }); 226 227 test('supports multiple statuses', () => { 228 const filter = parseTestResultSearchQuery('rstatus:pass,fail'); 229 const filtered = variants.filter(filter); 230 expect(filtered).toEqual([variant1, variant2, variant3, variant5]); 231 }); 232 233 test('should work with negation', () => { 234 const filter = parseTestResultSearchQuery('-rstatus:pass'); 235 const filtered = variants.filter(filter); 236 expect(filtered).toEqual([ 237 variant1, 238 variant2, 239 variant4, 240 variant6, 241 variant7, 242 ]); 243 }); 244 }); 245 246 describe('ExactID query', () => { 247 test('should only keep tests with the same ID', () => { 248 const filter = parseTestResultSearchQuery( 249 'ExactID:invocation-a/test-suite-b/test-5', 250 ); 251 const filtered = variants.filter(filter); 252 expect(filtered).toEqual([variant6]); 253 }); 254 255 test('should be case sensitive', () => { 256 const filter = parseTestResultSearchQuery( 257 'ExactID:invocation-a/test-suite-B/test-5', 258 ); 259 const filtered = variants.filter(filter); 260 expect(filtered).toEqual([variant5]); 261 }); 262 263 test('should work with negation', () => { 264 const filter = parseTestResultSearchQuery( 265 '-ExactID:invocation-a/test-suite-b/test-5', 266 ); 267 const filtered = variants.filter(filter); 268 expect(filtered).toEqual([ 269 variant1, 270 variant2, 271 variant3, 272 variant4, 273 variant5, 274 variant7, 275 ]); 276 }); 277 }); 278 279 describe('V query', () => { 280 test('should filter out variants with no matching variant key-value pair', () => { 281 const filter = parseTestResultSearchQuery('v:key1=val1'); 282 const filtered = variants.filter(filter); 283 expect(filtered).toEqual([variant1]); 284 }); 285 286 test("should support variant value with '=' in it", () => { 287 const filter = parseTestResultSearchQuery('v:key2=val3%3Dval'); 288 const filtered = variants.filter(filter); 289 expect(filtered).toEqual([variant7]); 290 }); 291 292 test('should support filter with only variant key', () => { 293 const filter = parseTestResultSearchQuery('v:key2'); 294 const filtered = variants.filter(filter); 295 expect(filtered).toEqual([variant5, variant6, variant7]); 296 }); 297 298 test('should work with negation', () => { 299 const filter = parseTestResultSearchQuery('-v:key1=val1'); 300 const filtered = variants.filter(filter); 301 expect(filtered).toEqual([ 302 variant2, 303 variant3, 304 variant4, 305 variant5, 306 variant6, 307 variant7, 308 ]); 309 }); 310 }); 311 312 describe('VHash query', () => { 313 test('should filter out variants with no matching variant hash', () => { 314 const filter = parseTestResultSearchQuery('vhash:key1:val1'); 315 const filtered = variants.filter(filter); 316 expect(filtered).toEqual([variant1]); 317 }); 318 319 test('should work with negation', () => { 320 const filter = parseTestResultSearchQuery('-vhash:key1:val1'); 321 const filtered = variants.filter(filter); 322 expect(filtered).toEqual([ 323 variant2, 324 variant3, 325 variant4, 326 variant5, 327 variant6, 328 variant7, 329 ]); 330 }); 331 }); 332 333 describe('Tag query', () => { 334 test('should filter out variants with no matching tag key-value pair', () => { 335 const filter = parseTestResultSearchQuery('tag:tag-key-1=tag-val-1'); 336 const filtered = variants.filter(filter); 337 expect(filtered).toEqual([variant1]); 338 }); 339 340 test("should support tag value with '=' in it", () => { 341 const filter = parseTestResultSearchQuery('tag:tag-key-1=tag-val-1=1'); 342 const filtered = variants.filter(filter); 343 expect(filtered).toEqual([variant1]); 344 }); 345 346 test('should support filter with only tag key', () => { 347 const filter = parseTestResultSearchQuery('tag:tag-key-1'); 348 const filtered = variants.filter(filter); 349 expect(filtered).toEqual([variant1, variant2]); 350 }); 351 352 test('should work with negation', () => { 353 const filter = parseTestResultSearchQuery('-tag:tag-key-1=tag-val-1'); 354 const filtered = variants.filter(filter); 355 expect(filtered).toEqual([ 356 variant2, 357 variant3, 358 variant4, 359 variant5, 360 variant6, 361 variant7, 362 ]); 363 }); 364 365 test('should support duplicated tag key', () => { 366 const filter = parseTestResultSearchQuery( 367 '-tag:duplicated-tag-key=second-tag-val', 368 ); 369 const filtered = variants.filter(filter); 370 expect(filtered).toEqual([ 371 variant1, 372 variant3, 373 variant4, 374 variant5, 375 variant6, 376 variant7, 377 ]); 378 }); 379 }); 380 381 describe('Name query', () => { 382 test("should filter out variants whose test name doesn't match the search text", () => { 383 const filter = parseTestResultSearchQuery('Name:test-name'); 384 const filtered = variants.filter(filter); 385 expect(filtered).toEqual([variant1, variant2]); 386 }); 387 388 test('should be case insensitive', () => { 389 const filter = parseTestResultSearchQuery('Name:test-NAME'); 390 const filtered = variants.filter(filter); 391 expect(filtered).toEqual([variant1, variant2]); 392 }); 393 394 test('should work with negation', () => { 395 const filter = parseTestResultSearchQuery('-Name:test-name-1'); 396 const filtered = variants.filter(filter); 397 expect(filtered).toEqual([ 398 variant2, 399 variant3, 400 variant4, 401 variant5, 402 variant6, 403 variant7, 404 ]); 405 }); 406 }); 407 408 describe('Duration query', () => { 409 test('should filter out variants with no run that has the specified duration', () => { 410 const filter = parseTestResultSearchQuery('Duration:5-10'); 411 const filtered = variants.filter(filter); 412 expect(filtered).toEqual([variant1]); 413 }); 414 415 test('should support decimals', () => { 416 const filter = parseTestResultSearchQuery('Duration:5.5-10.5'); 417 const filtered = variants.filter(filter); 418 expect(filtered).toEqual([variant1]); 419 }); 420 421 test('should support omitting max duration', () => { 422 const filter = parseTestResultSearchQuery('Duration:5-'); 423 const filtered = variants.filter(filter); 424 expect(filtered).toEqual([variant1, variant2]); 425 }); 426 427 test('should work with negation', () => { 428 const filter = parseTestResultSearchQuery('-Duration:5-10'); 429 const filtered = variants.filter(filter); 430 expect(filtered).toEqual([ 431 variant2, 432 variant3, 433 variant4, 434 variant5, 435 variant6, 436 variant7, 437 ]); 438 }); 439 }); 440 441 describe('ExactName query', () => { 442 test('should only keep tests with the same name', () => { 443 const filter = parseTestResultSearchQuery('ExactName:test'); 444 const filtered = variants.filter(filter); 445 expect(filtered).toEqual([variant3]); 446 }); 447 448 test('should be case sensitive', () => { 449 const filter = parseTestResultSearchQuery('ExactName:tesT'); 450 const filtered = variants.filter(filter); 451 expect(filtered).toEqual([]); 452 }); 453 454 test('should work with negation', () => { 455 const filter = parseTestResultSearchQuery('-ExactName:test'); 456 const filtered = variants.filter(filter); 457 expect(filtered).toEqual([ 458 variant1, 459 variant2, 460 variant4, 461 variant5, 462 variant6, 463 variant7, 464 ]); 465 }); 466 }); 467 468 describe('multiple queries', () => { 469 test('should be able to combine different types of query', () => { 470 const filter = parseTestResultSearchQuery('rstatus:pass id:test-3'); 471 const filtered = variants.filter(filter); 472 expect(filtered).toEqual([variant3]); 473 }); 474 475 test('should be able to combine normal and negative queries', () => { 476 const filter = parseTestResultSearchQuery('rstatus:pass -rstatus:fail'); 477 const filtered = variants.filter(filter); 478 expect(filtered).toEqual([variant5]); 479 }); 480 }); 481 }); 482 483 describe('suggestTestResultSearchQuery', () => { 484 test('should give user some suggestions when the query is empty', () => { 485 const suggestions1 = suggestTestResultSearchQuery(''); 486 expect(suggestions1.length).not.toStrictEqual(0); 487 }); 488 489 test('should not give suggestions when the sub-query is empty', () => { 490 const suggestions1 = suggestTestResultSearchQuery('Status:UNEXPECTED '); 491 expect(suggestions1.length).toStrictEqual(0); 492 }); 493 494 test('should give user suggestions based on the last sub-query', () => { 495 const suggestions1 = suggestTestResultSearchQuery('unexpected Pass'); 496 expect(suggestions1.find((s) => s.value === 'RStatus:Pass')).toBeDefined(); 497 expect(suggestions1.find((s) => s.value === '-RStatus:Pass')).toBeDefined(); 498 expect( 499 suggestions1.find((s) => s.value === 'Status:UNEXPECTED'), 500 ).toBeUndefined(); 501 expect( 502 suggestions1.find((s) => s.value === '-Status:UNEXPECTED'), 503 ).toBeUndefined(); 504 }); 505 506 test('should suggest run status query with matching status', () => { 507 const suggestions1 = suggestTestResultSearchQuery('Pass'); 508 expect(suggestions1.find((s) => s.value === 'RStatus:Pass')).toBeDefined(); 509 expect(suggestions1.find((s) => s.value === '-RStatus:Pass')).toBeDefined(); 510 511 const suggestions2 = suggestTestResultSearchQuery('Fail'); 512 expect(suggestions2.find((s) => s.value === 'RStatus:Fail')).toBeDefined(); 513 expect(suggestions2.find((s) => s.value === '-RStatus:Fail')).toBeDefined(); 514 515 const suggestions3 = suggestTestResultSearchQuery('Crash'); 516 expect(suggestions3.find((s) => s.value === 'RStatus:Crash')).toBeDefined(); 517 expect( 518 suggestions3.find((s) => s.value === '-RStatus:Crash'), 519 ).toBeDefined(); 520 521 const suggestions4 = suggestTestResultSearchQuery('Abort'); 522 expect(suggestions4.find((s) => s.value === 'RStatus:Abort')).toBeDefined(); 523 expect( 524 suggestions4.find((s) => s.value === '-RStatus:Abort'), 525 ).toBeDefined(); 526 527 const suggestions5 = suggestTestResultSearchQuery('Skip'); 528 expect(suggestions5.find((s) => s.value === 'RStatus:Skip')).toBeDefined(); 529 expect(suggestions5.find((s) => s.value === '-RStatus:Skip')).toBeDefined(); 530 }); 531 532 test('should not suggest run status query with a different status', () => { 533 const suggestions1 = suggestTestResultSearchQuery('Pass'); 534 expect( 535 suggestions1.find((s) => s.value === 'RStatus:Fail'), 536 ).toBeUndefined(); 537 expect( 538 suggestions1.find((s) => s.value === '-RStatus:Fail'), 539 ).toBeUndefined(); 540 expect( 541 suggestions1.find((s) => s.value === 'RStatus:Crash'), 542 ).toBeUndefined(); 543 expect( 544 suggestions1.find((s) => s.value === '-RStatus:Crash'), 545 ).toBeUndefined(); 546 expect( 547 suggestions1.find((s) => s.value === 'RStatus:Abort'), 548 ).toBeUndefined(); 549 expect( 550 suggestions1.find((s) => s.value === '-RStatus:Abort'), 551 ).toBeUndefined(); 552 expect( 553 suggestions1.find((s) => s.value === 'RStatus:Skip'), 554 ).toBeUndefined(); 555 expect( 556 suggestions1.find((s) => s.value === '-RStatus:Skip'), 557 ).toBeUndefined(); 558 }); 559 560 test('should suggest variant status query with matching status', () => { 561 const suggestions1 = suggestTestResultSearchQuery('unexpected'); 562 expect( 563 suggestions1.find((s) => s.value === 'Status:UNEXPECTED'), 564 ).toBeDefined(); 565 expect( 566 suggestions1.find((s) => s.value === '-Status:UNEXPECTED'), 567 ).toBeDefined(); 568 569 const suggestions2 = suggestTestResultSearchQuery('flaky'); 570 expect(suggestions2.find((s) => s.value === 'Status:FLAKY')).toBeDefined(); 571 expect(suggestions2.find((s) => s.value === '-Status:FLAKY')).toBeDefined(); 572 573 const suggestions3 = suggestTestResultSearchQuery('exonerated'); 574 expect( 575 suggestions3.find((s) => s.value === 'Status:EXONERATED'), 576 ).toBeDefined(); 577 expect( 578 suggestions3.find((s) => s.value === '-Status:EXONERATED'), 579 ).toBeDefined(); 580 581 const suggestions4 = suggestTestResultSearchQuery('expected'); 582 expect( 583 suggestions4.find((s) => s.value === 'Status:EXPECTED'), 584 ).toBeDefined(); 585 expect( 586 suggestions4.find((s) => s.value === '-Status:EXPECTED'), 587 ).toBeDefined(); 588 }); 589 590 test('should not suggest variant status query with a different status', () => { 591 const suggestions1 = suggestTestResultSearchQuery('UNEXPECTED'); 592 expect( 593 suggestions1.find((s) => s.value === 'Status:FLAKY'), 594 ).toBeUndefined(); 595 expect( 596 suggestions1.find((s) => s.value === '-Status:FLAKY'), 597 ).toBeUndefined(); 598 expect( 599 suggestions1.find((s) => s.value === 'Status:EXONERATED'), 600 ).toBeUndefined(); 601 expect( 602 suggestions1.find((s) => s.value === '-Status:EXONERATED'), 603 ).toBeUndefined(); 604 expect( 605 suggestions1.find((s) => s.value === 'Status:EXPECTED'), 606 ).toBeUndefined(); 607 expect( 608 suggestions1.find((s) => s.value === '-Status:EXPECTED'), 609 ).toBeUndefined(); 610 }); 611 612 test('suggestion should be case insensitive', () => { 613 const suggestions1 = suggestTestResultSearchQuery('PASS'); 614 expect(suggestions1.find((s) => s.value === 'RStatus:Pass')).toBeDefined(); 615 expect(suggestions1.find((s) => s.value === '-RStatus:Pass')).toBeDefined(); 616 617 const suggestions2 = suggestTestResultSearchQuery('fail'); 618 expect(suggestions2.find((s) => s.value === 'RStatus:Fail')).toBeDefined(); 619 expect(suggestions2.find((s) => s.value === '-RStatus:Fail')).toBeDefined(); 620 621 const suggestions3 = suggestTestResultSearchQuery('CrAsH'); 622 expect(suggestions3.find((s) => s.value === 'RStatus:Crash')).toBeDefined(); 623 expect( 624 suggestions3.find((s) => s.value === '-RStatus:Crash'), 625 ).toBeDefined(); 626 627 const suggestions4 = suggestTestResultSearchQuery('Abort'); 628 expect(suggestions4.find((s) => s.value === 'RStatus:Abort')).toBeDefined(); 629 expect( 630 suggestions4.find((s) => s.value === '-RStatus:Abort'), 631 ).toBeDefined(); 632 633 const suggestions5 = suggestTestResultSearchQuery('sKIP'); 634 expect(suggestions5.find((s) => s.value === 'RStatus:Skip')).toBeDefined(); 635 expect(suggestions5.find((s) => s.value === '-RStatus:Skip')).toBeDefined(); 636 }); 637 638 test('should suggest ID query', () => { 639 const suggestions1 = suggestTestResultSearchQuery('ranDom'); 640 expect(suggestions1.find((s) => s.value === 'ID:ranDom')).toBeDefined(); 641 expect(suggestions1.find((s) => s.value === '-ID:ranDom')).toBeDefined(); 642 }); 643 644 test('should suggest ID query when the query prefix is ID:', () => { 645 const suggestions1 = suggestTestResultSearchQuery('ID:pass'); 646 expect(suggestions1.find((s) => s.value === 'ID:pass')).toBeDefined(); 647 expect(suggestions1.find((s) => s.value === '-ID:pass')).toBeDefined(); 648 649 const suggestions2 = suggestTestResultSearchQuery('-ID:pass'); 650 // When user explicitly typed negative query, don't suggest positive query. 651 expect(suggestions2.find((s) => s.value === 'ID:pass')).toBeUndefined(); 652 expect(suggestions2.find((s) => s.value === '-ID:pass')).toBeDefined(); 653 }); 654 655 test('should suggest ID query when the query type is a substring of ID:', () => { 656 const suggestions1 = suggestTestResultSearchQuery('i'); 657 expect(suggestions1.find((s) => s.value === 'ID:')).toBeDefined(); 658 expect(suggestions1.find((s) => s.value === '-ID:')).toBeDefined(); 659 }); 660 661 test('should suggest ID query even when there are other matching queries', () => { 662 const suggestions1 = suggestTestResultSearchQuery('fail'); 663 expect(suggestions1.find((s) => s.value === 'RStatus:Fail')).toBeDefined(); 664 expect(suggestions1.find((s) => s.value === '-RStatus:Fail')).toBeDefined(); 665 expect(suggestions1.find((s) => s.value === 'ID:fail')).toBeDefined(); 666 expect(suggestions1.find((s) => s.value === '-ID:fail')).toBeDefined(); 667 }); 668 669 test('should suggest ExactID query when the query prefix is ExactID:', () => { 670 const suggestions1 = suggestTestResultSearchQuery('ExactID:pass'); 671 expect(suggestions1.find((s) => s.value === 'ExactID:pass')).toBeDefined(); 672 expect(suggestions1.find((s) => s.value === '-ExactID:pass')).toBeDefined(); 673 674 const suggestions2 = suggestTestResultSearchQuery('-ExactID:pass'); 675 // When user explicitly typed negative query, don't suggest positive query. 676 expect( 677 suggestions2.find((s) => s.value === 'ExactID:pass'), 678 ).toBeUndefined(); 679 expect(suggestions2.find((s) => s.value === '-ExactID:pass')).toBeDefined(); 680 }); 681 682 test('should suggest ExactID query when the query type is a substring of ExactID:', () => { 683 const suggestions1 = suggestTestResultSearchQuery('xact'); 684 expect(suggestions1.find((s) => s.value === 'ExactID:')).toBeDefined(); 685 expect(suggestions1.find((s) => s.value === '-ExactID:')).toBeDefined(); 686 }); 687 688 test('should suggest V query when the query prefix is V:', () => { 689 const suggestions1 = suggestTestResultSearchQuery('V:test_suite'); 690 expect(suggestions1.find((s) => s.value === 'V:test_suite')).toBeDefined(); 691 expect(suggestions1.find((s) => s.value === '-V:test_suite')).toBeDefined(); 692 693 const suggestions2 = suggestTestResultSearchQuery('-V:test_suite'); 694 // When user explicitly typed negative query, don't suggest positive query. 695 expect( 696 suggestions2.find((s) => s.value === 'V:test_suite'), 697 ).toBeUndefined(); 698 expect(suggestions2.find((s) => s.value === '-V:test_suite')).toBeDefined(); 699 }); 700 701 test('should suggest Tag query when the query prefix is Tag:', () => { 702 const suggestions1 = suggestTestResultSearchQuery('Tag:tag_key'); 703 expect(suggestions1.find((s) => s.value === 'Tag:tag_key')).toBeDefined(); 704 expect(suggestions1.find((s) => s.value === '-Tag:tag_key')).toBeDefined(); 705 706 const suggestions2 = suggestTestResultSearchQuery('-Tag:tag_key'); 707 // When user explicitly typed negative query, don't suggest positive query. 708 expect(suggestions2.find((s) => s.value === 'Tag:tag_key')).toBeUndefined(); 709 expect(suggestions2.find((s) => s.value === '-Tag:tag_key')).toBeDefined(); 710 }); 711 712 test('should suggest Duration query when the query prefix is Duration:', () => { 713 const suggestions1 = suggestTestResultSearchQuery('Duration:10'); 714 expect(suggestions1.find((s) => s.value === 'Duration:10')).toBeDefined(); 715 expect(suggestions1.find((s) => s.value === '-Duration:10')).toBeDefined(); 716 717 const suggestions2 = suggestTestResultSearchQuery('-Duration:10'); 718 // When user explicitly typed negative query, don't suggest positive query. 719 expect(suggestions2.find((s) => s.value === 'Duration:10')).toBeUndefined(); 720 expect(suggestions2.find((s) => s.value === '-Duration:10')).toBeDefined(); 721 }); 722 723 test('should suggest VHash query when the query prefix is VHash:', () => { 724 const suggestions1 = suggestTestResultSearchQuery('VHash:pass'); 725 expect(suggestions1.find((s) => s.value === 'VHash:pass')).toBeDefined(); 726 expect(suggestions1.find((s) => s.value === '-VHash:pass')).toBeDefined(); 727 728 const suggestions2 = suggestTestResultSearchQuery('-VHash:pass'); 729 // When user explicitly typed negative query, don't suggest positive query. 730 expect(suggestions2.find((s) => s.value === 'VHash:pass')).toBeUndefined(); 731 expect(suggestions2.find((s) => s.value === '-VHash:pass')).toBeDefined(); 732 }); 733 734 test('should suggest VHash query when the query type is a substring of VHash:', () => { 735 const suggestions1 = suggestTestResultSearchQuery('hash'); 736 expect(suggestions1.find((s) => s.value === 'VHash:')).toBeDefined(); 737 expect(suggestions1.find((s) => s.value === '-VHash:')).toBeDefined(); 738 }); 739 });