1const node = document.querySelector('#explore'); 2 3const buildFragment = (url: string): Node => { 4 const fragment = ` 5 <tr class="additional_info"> 6 <th data-key="additional_info"><!---->additional_info<!----></th> 7 <!----> 8 <td> 9 <!----> 10 <div class="" data-key="additional_info" data-value="${url}"> 11 <a href="${url}" target="_blank">Additional Information</a> 12 </div><!----> 13 </td><!----> 14 <td> 15 <add-icon-sk data-key="additional_info" data-values="["${url}"]"> 16 <svg class="icon-sk-svg" 17 xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> 18 <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path> 19 </svg></add-icon-sk> 20 </td><!----> 21 </tr> 22 `; 23 const template = document.createElement('template'); 24 template.innerHTML = fragment; 25 return template.content.querySelector('.additional_info')!; 26}; 27 28if (node) { 29 const callback = () => { 30 const device = document.querySelector("div[data-key='device_name']"); 31 // As more tests are selected in the Skia UI, additional nodes are 32 // added to the below NodeList, but only one of them is actually visible. 33 const tests = document.querySelectorAll("div[data-key='test']"); 34 let selectedTest: Element | null = null; 35 if (tests) { 36 // Checking if an element is visible triggers a layout. So stop listening for 37 // mutation events temporarily. 38 observer.disconnect(); 39 for (let i = 0; i < tests.length; i += 1) { 40 const test = tests[i]; 41 const visibility = test.checkVisibility(); 42 if (visibility) { 43 selectedTest = test; 44 break; 45 } 46 } 47 // Start observing for new changes. 48 observer.observe(node, { 49 subtree: true, 50 childList: true, 51 attributes: true 52 }); 53 } 54 const logEntry = document.querySelector('#logEntry'); 55 if (device && selectedTest) { 56 const deviceName = device.getAttribute('data-value'); 57 const testName = selectedTest.getAttribute('data-value'); 58 DEVICE_NAME = deviceName; 59 TEST_NAME = testName; 60 } 61 if (logEntry) { 62 const content = logEntry.textContent; 63 if (content) { 64 const matches = content.match(/jump-to-build[/](\d+)/); 65 if (matches) { 66 BUILD = matches[1]; 67 updateUrl(); 68 } 69 } 70 } 71 }; 72 73 const updateUrl = () => { 74 observer.disconnect(); 75 const url = buildUrl(); 76 if (url) { 77 // Populate details 78 const details = document.querySelector('div#details table.clickable_values tbody'); 79 if (details) { 80 const existing = details.querySelector('.additional_info'); 81 if (existing) { 82 existing.remove(); 83 } 84 const row = buildFragment(url); 85 details.appendChild(row); 86 } 87 } 88 observer.observe(node, { 89 subtree: true, 90 childList: true, 91 attributes: true 92 }); 93 }; 94 95 const observer = new MutationObserver(callback); 96 observer.observe(node, { 97 subtree: true, 98 childList: true, 99 attributes: true 100 }); 101} 102 103// Globals 104let DEVICE_NAME: string | null = null; 105let BUILD: string | null = null; 106let TEST_NAME: string | null = null; 107 108const buildUrl = (): string | null => { 109 if (BUILD && TEST_NAME && DEVICE_NAME) { 110 const encodedBuild = encodeURIComponent(BUILD); 111 const encodedTestName = encodeURIComponent(TEST_NAME); 112 const encodedDeviceName = encodeURIComponent(DEVICE_NAME); 113 return `https://androidx.dev/tests/artifacts/builds/${encodedBuild}?testName=${encodedTestName}&device=${encodedDeviceName}`; 114 } 115 return null; 116}; 117