github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/webapp/components/test/test-search.html (about) 1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script> 6 <script src="../../node_modules/wct-browser-legacy/browser.js"></script> 7 8 <script type="module" src="../test-search.js"></script> 9 </head> 10 <body> 11 <test-fixture id="test-search-fixture"> 12 <template> 13 <test-search></test-search> 14 </template> 15 </test-fixture> 16 17 <script type="module"> 18 import { AllBrowserNames } from '../product-info.js'; 19 import { TestSearch } from '../test-search.js'; 20 21 suite('<test-search>', () => { 22 suite('Parser/interpreter', () => { 23 const assertQueryParse = (query, structuredQuery) => { 24 const G = TestSearch.QUERY_GRAMMAR; 25 const S = TestSearch.QUERY_SEMANTICS; 26 const p = G.match(query); 27 assert.isTrue(p.succeeded(), p.message); 28 assert.deepEqual(structuredQuery, S(p).eval()); 29 }; 30 31 const assertQueryFail = (query) => { 32 const G = TestSearch.QUERY_GRAMMAR; 33 const p = G.match(query); 34 assert.isFalse(p.succeeded(), p.message); 35 }; 36 37 test('empty pattern', () => { 38 assertQueryParse('', {exists: [{pattern: ''}]}); 39 }); 40 41 test('simple pattern', () => { 42 assertQueryParse('2dcontext', {exists: [{pattern: '2dcontext'}]}); 43 assertQueryParse( 44 '/2dcontext/building-paths/canvas_complexshapes_arcto_001.htm', 45 {exists: [{pattern: '/2dcontext/building-paths/canvas_complexshapes_arcto_001.htm'}]} 46 ); 47 }); 48 49 suite('quoted pattern', () => { 50 test('basic', () => { 51 assertQueryParse('"foo"', {exists: [{pattern: 'foo'}]}); 52 assertQueryParse('"/foo.html"', {exists: [{pattern: '/foo.html'}]}); 53 }); 54 test('complex', () => { 55 assertQueryParse('"/foo.html?exclude=(Document|window|HTML.*)"', {exists: [{pattern: '/foo.html?exclude=(Document|window|HTML.*)'}]}); 56 }); 57 }); 58 59 test('subtest', () => { 60 assertQueryParse('subtest:idl_test', {exists: [{subtest: 'idl_test'}]}); 61 }); 62 63 test('subtest_quoted', () => { 64 assertQueryParse('subtest:"idl_test setup"', {exists: [{subtest: 'idl_test setup'}]}); 65 }); 66 67 test('path', () => { 68 assertQueryParse('path:/dom/', {exists: [{path: '/dom/'}]}); 69 }); 70 71 test('path quoted', () => { 72 assertQueryParse('path:"/foo.html?exclude=(Document|window|HTML.*)"', {exists: [{path: '/foo.html?exclude=(Document|window|HTML.*)'}]}); 73 }); 74 75 test('versioned browser', () => { 76 assertQueryParse('chrome-64:fail', {exists: [{product: 'chrome-64', status: 'FAIL'}]}); 77 }); 78 79 test('test status', () => { 80 assertQueryParse('status:missing', {exists: [{status: 'UNKNOWN'}]}); 81 assertQueryParse('status:!missing', {exists: [{status: {not: 'UNKNOWN'}}]}); 82 assertQueryParse('sTaTuS:UnKnOwN', {exists: [{status: 'UNKNOWN'}]}); 83 }); 84 85 test('test status eq', () => { 86 assertQueryParse('sTaTuS:oK', {exists: [{status: 'OK'}]}); 87 }); 88 89 test('test status neq', () => { 90 assertQueryParse('StAtUs:!FaIl', {exists: [{status: {not: 'FAIL'}}]}); 91 }); 92 93 test('browser test status eq', () => { 94 // All known browsers. 95 for (const browser of AllBrowserNames) { 96 assertQueryParse(browser + ':ok', {exists: [{product: browser, status: 'OK'}]}); 97 } 98 99 // Make sure unknown ones don't parse. 100 assertQueryFail('darkmatter:ok'); 101 102 // Comparisons are case-insensitive. 103 assertQueryParse('cHrOmE:oK', {exists: [{product: 'chrome', status: 'OK'}]}); 104 }); 105 106 test('browser test status neq', () => { 107 assertQueryParse('sAfArI:!FaIl', {exists: [{product: 'safari', status: {not: 'FAIL'}}]}); 108 }); 109 110 test('pattern + test status', () => { 111 assertQueryParse('cssom firefox:timeout', { 112 exists: [ 113 {pattern: 'cssom'}, 114 {product: 'firefox', status: 'TIMEOUT'}, 115 ], 116 }); 117 118 assertQueryParse('cssom AND firefox:timeout', { 119 exists: [{ 120 and: [ 121 {pattern: 'cssom'}, 122 {product: 'firefox', status: 'TIMEOUT'}, 123 ] 124 }], 125 }); 126 127 assertQueryParse('cssom & firefox:timeout', { 128 exists: [{ 129 and: [ 130 {pattern: 'cssom'}, 131 {product: 'firefox', status: 'TIMEOUT'}, 132 ] 133 }], 134 }); 135 }); 136 137 test('pattern | test status', () => { 138 assertQueryParse('cssom or firefox:timeout', { 139 exists: [{ 140 or: [ 141 {pattern: 'cssom'}, 142 {product: 'firefox', status: 'TIMEOUT'}, 143 ] 144 }], 145 }); 146 }); 147 148 test('implicit and, or', () => { 149 assertQueryParse('a b or c', { 150 exists: [ 151 {pattern: 'a'}, 152 { 153 or: [ 154 {pattern: 'b'}, 155 {pattern: 'c'}, 156 ], 157 }, 158 ], 159 }); 160 }); 161 162 test('explicit and, or', () => { 163 assertQueryParse('a and b or c', { 164 exists: [{ 165 or: [ 166 { 167 and: [ 168 {pattern: 'a'}, 169 {pattern: 'b'}, 170 ], 171 }, 172 {pattern: 'c'}, 173 ], 174 }] 175 }); 176 }); 177 178 test('parens', () => { 179 assertQueryParse('a and ( b or c )', { 180 exists: [{ 181 and: [ 182 {pattern: 'a'}, 183 { 184 or: [ 185 {pattern: 'b'}, 186 {pattern: 'c'}, 187 ], 188 }, 189 ], 190 }] 191 }); 192 193 assertQueryParse('a or ( b and c )', { 194 exists: [{ 195 or: [ 196 {pattern: 'a'}, 197 { 198 and: [ 199 {pattern: 'b'}, 200 {pattern: 'c'}, 201 ], 202 }, 203 ], 204 }], 205 }); 206 }); 207 208 test('nested or/and/not', () => { 209 assertQueryParse('(chrome:pass or edge:pass) (firefox:!pass and firefox:!ok)', { 210 exists: [ 211 { 212 or: [ 213 {product: 'chrome', status: 'PASS'}, 214 {product: 'edge', status: 'PASS'}, 215 ] 216 }, 217 { 218 and: [ 219 {product: 'firefox', status: { not: 'PASS'} }, 220 {product: 'firefox', status: { not: 'OK'} }, 221 ], 222 }, 223 ] 224 }); 225 assertQueryParse( 226 'chrome:pass (!(firefox:pass or firefox:ok) and !(safari:pass or safari:ok) and !(edge:pass or edge:ok))', 227 { 228 exists: [ 229 {product: 'chrome', status: 'PASS'}, 230 { 231 and: ['firefox','safari','edge'].map(b => { 232 return { 233 not: { 234 or: [ 235 {product: b, status: 'PASS'}, 236 {product: b, status: 'OK'}, 237 ] 238 } 239 }; 240 }) 241 }, 242 ] 243 } 244 ); 245 }); 246 247 test('or of and of and', () => { 248 assertQueryParse('firefox:pass a | chrome:fail and ( b & c )', { 249 exists: [ 250 {product: 'firefox', status: 'PASS'}, 251 { 252 or: [ 253 {pattern: 'a'}, 254 { 255 and: [ 256 {product: 'chrome', status: 'FAIL'}, 257 { 258 and: [ 259 {pattern: 'b'}, 260 {pattern: 'c'}, 261 ], 262 }, 263 ], 264 }, 265 ], 266 }, 267 ] 268 }); 269 }); 270 271 test('exists', () => { 272 assertQueryParse('exists(status:PASS)', { 273 exists: [{status: 'PASS'}], 274 }); 275 }); 276 277 test('all', () => { 278 assertQueryParse('all(status:!PASS status:!OK)', { 279 all: [ 280 {status: {not: 'PASS'} }, 281 {status: {not: 'OK'} }, 282 ], 283 }); 284 }); 285 286 test('none', () => { 287 assertQueryParse('none(status:PASS or status:OK)', { 288 none: [{ 289 or: [ 290 {status: 'PASS'}, 291 {status: 'OK'}, 292 ] 293 }], 294 }); 295 }); 296 297 test('sequential', () => { 298 // Canon: flip-flopping, which is usually flakiness: 299 // A pass turning into a fail on the next run, and a non-pass turning to a pass on the next run. 300 assertQueryParse('seq(status:PASS status:!PASS)', { 301 sequential: [ 302 {status: 'PASS'}, 303 {status: {not: 'PASS'} }, 304 ] 305 }); 306 // Canon: flip-flopping, which is usually flakiness: 307 // A pass turning into a fail on the next run, and a non-pass turning to a pass on the next run. 308 assertQueryParse('seq((status:!PASS and status:!OK) (status:PASS or status:OK)) seq((status:PASS or status:OK) (status:!PASS and status:!OK))', { 309 and: [ 310 { sequential: [ 311 { 312 and: [ 313 {status: {not: 'PASS'}}, 314 {status: {not: 'OK'}}, 315 ], 316 }, 317 { or: [{status: 'PASS'}, {status: 'OK'}] }, 318 ]}, 319 { sequential: [ 320 { or: [{status: 'PASS'}, {status: 'OK'}] }, 321 { 322 and: [ 323 {status: {not: 'PASS'}}, 324 {status: {not: 'OK'}}, 325 ], 326 }, 327 ]}, 328 ], 329 }); 330 }); 331 332 test('is', () => { 333 assertQueryParse('is:different', { 334 exists: [{ is: 'different' }] 335 }); 336 assertQueryParse('is:tentative', { 337 exists: [{ is: 'tentative' }] 338 }); 339 assertQueryParse('is:optional', { 340 exists: [{ is: 'optional' }] 341 }); 342 }); 343 344 test('count', () => { 345 assertQueryParse('count:5(status:PASS or status:OK)', { 346 count: 5, 347 where: { 348 or: [{status: 'PASS'}, {status: 'OK'}], 349 }, 350 }); 351 for (const [atom, count] of [['three', 3], ['two', 2], ['one', 1]]) { 352 assertQueryParse(`${atom}(status:!PASS and status:!OK)`, { 353 count, 354 where: { 355 and: [{status: {not: 'PASS'}}, {status: {not: 'OK'}}], 356 }, 357 }); 358 } 359 assertQueryParse('count=5(status:PASS)', { count: 5, where: {status: 'PASS' }}); 360 assertQueryParse('count>1(status:PASS)', { moreThan: 1, where: {status: 'PASS' }}); 361 assertQueryParse('count>=2(status:PASS)', { moreThan: 1, where: {status: 'PASS' }}); 362 assertQueryParse('count<3(status:PASS)', { lessThan: 3, where: {status: 'PASS' }}); 363 assertQueryParse('count<=2(status:PASS)', { lessThan: 3, where: {status: 'PASS' }}); 364 365 assertQueryParse('count:=5(status:PASS)', { count: 5, where: {status: 'PASS' }}); 366 assertQueryParse('count:>1(status:PASS)', { moreThan: 1, where: {status: 'PASS' }}); 367 assertQueryParse('count:>=2(status:PASS)', { moreThan: 1, where: {status: 'PASS' }}); 368 assertQueryParse('count:<3(status:PASS)', { lessThan: 3, where: {status: 'PASS' }}); 369 assertQueryParse('count:<=2(status:PASS)', { lessThan: 3, where: {status: 'PASS' }}); 370 }); 371 372 test('simple link search', () => { 373 assertQueryParse('link:2dcontext', {exists: [{link: '2dcontext'}]}); 374 assertQueryParse( 375 'link:bugs.chromium.org/p/chromium/issues/detail', 376 {exists: [{link: 'bugs.chromium.org/p/chromium/issues/detail'}]} 377 ); 378 }); 379 380 test('test status | link', () => { 381 assertQueryParse('firefox:timeout link:chromium.bug', { 382 exists: [ 383 {product: 'firefox', status: 'TIMEOUT'}, 384 {link: 'chromium.bug'}, 385 ], 386 }); 387 388 assertQueryParse('link:chromium.bug firefox:timeout', { 389 exists: [ 390 {link: 'chromium.bug'}, 391 {product: 'firefox', status: 'TIMEOUT'}, 392 ], 393 }); 394 }); 395 396 test('test status and not link', () => { 397 assertQueryParse('firefox:timeout & not link:chromium.bug', { 398 exists: [ 399 { 400 and: [ 401 { product: 'firefox', status: 'TIMEOUT' }, 402 { 403 not: 404 { link: 'chromium.bug' }, 405 }, 406 ], 407 }], 408 }); 409 }); 410 411 test('simple triaged search', () => { 412 assertQueryParse('triaged:chrome', { exists: [{ triaged: 'chrome' }] }); 413 }); 414 415 test('test status and triaged', () => { 416 assertQueryParse('firefox:timeout triaged:firefox', { 417 exists: [ 418 { product: 'firefox', status: 'TIMEOUT' }, 419 { triaged: 'firefox' }, 420 ], 421 }); 422 }); 423 424 test('test-level triaged search', () => { 425 assertQueryParse('triaged:test-issue', { exists: [{ triaged: '' }] }); 426 }); 427 428 test('metadata label search', () => { 429 assertQueryParse('label:interop123', { exists: [{ label: 'interop123' }] }); 430 }); 431 432 test('web feature search', () => { 433 assertQueryParse('feature:grid', { exists: [{ feature: 'grid' }] }); 434 }); 435 436 test('multi-root', () => { 437 assertQueryParse('none(status:missing) count>0(status:!pass)', { 438 and: [ 439 { none: [{ status: 'UNKNOWN' }] }, 440 { moreThan: 0, where: { status: { not: 'PASS' } } }, 441 ] 442 }); 443 assertQueryParse('all(status:pass) or none(status:pass)', { 444 or: [ 445 { all: [{ status: 'PASS' }] }, 446 { none: [{ status: 'PASS' }] }, 447 ] 448 }); 449 assertQueryParse('idlharness all(status:fail)', { 450 and: [ 451 { exists: [{ pattern: 'idlharness' }] }, 452 { all: [{ status: 'FAIL' }] }, 453 ] 454 }); 455 assertQueryParse('2dcontext and all(status:fail)', { 456 and: [ 457 { exists: [{ pattern: '2dcontext' }] }, 458 { all: [{ status: 'FAIL' }] }, 459 ] 460 }); 461 }); 462 }); 463 }); 464 </script> 465 </body> 466 </html>