Last active
November 11, 2023 16:40
-
-
Save sebastianrothbucher/29a0c126a296792c1061ce7b612da1ee to your computer and use it in GitHub Desktop.
Track fetch API for more stable tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const __legacyFetch = window.fetch; | |
let __openFetchPromises = 0; | |
let __waitForFetchPromise = Promise.resolve(); | |
let __waitForFetchResolve = null; | |
// need a function to instrument promise-ified methods from fetch down | |
function __instrumentPromise(legacyThis, legacyFct, legacyFctName) { | |
return function() { | |
if (__openFetchPromises === 0) { | |
__waitForFetchPromise = new Promise(resolve => __waitForFetchResolve = resolve); | |
} | |
const legacyRes = legacyFct.apply(legacyThis, arguments); | |
if (legacyRes instanceof Promise) { | |
__openFetchPromises++; | |
console.log('New ' + legacyFctName + ' - now: ' + __openFetchPromises); | |
return legacyRes.then((legacyPromiseRes) => { | |
// if there's a chance for new promises, instrument accordingly | |
Object.keys(legacyPromiseRes?.__proto__ || {}) | |
.filter(name => typeof(legacyPromiseRes[name]) === 'function') | |
.forEach(name => { | |
legacyPromiseRes[name] = __instrumentPromise(legacyPromiseRes, legacyPromiseRes[name], name); | |
}); | |
// handle this result itself | |
setTimeout(() => { // run ALL other handlers before declaring we're done | |
__openFetchPromises--; | |
console.log('Done handling ' + legacyFctName + ' - now: ' + __openFetchPromises); | |
if (__openFetchPromises === 0) { | |
console.log('Done with backend - firing our promise'); | |
__waitForFetchResolve(); | |
} | |
}); | |
return legacyPromiseRes; | |
}); | |
} else { | |
return legacyRes; | |
} | |
}; | |
} | |
window.__waitForFetch = async function() { | |
await __waitForFetchPromise; | |
}; | |
window.__hasNoOngoingFetch = function() { | |
return __openFetchPromises === 0; | |
}; | |
// finally insert our hook | |
window.fetch = __instrumentPromise(window, window.fetch, 'fetch'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { test, expect } from '@playwright/test'; | |
test('use flaky API', async ({ page }) => { | |
await page.goto('/'); | |
await page.locator('#bt').click(); | |
//await page.locator('//span[contains(string(.), "content two")]').waitFor(); // a TON is wrong with that: locators are hard to write (and do we really want a page model just for that?) - and an app error waits the whole 30s before firing! | |
await page.waitForFunction(() => (window as any).__hasNoOngoingFetch()); // so much better | |
await expect(page.locator('#spn1')).toContainText('content one', {timeout: -1}); | |
await expect(page.locator('#spn2')).toContainText(/content two/, {timeout: -1}); // try "three" here - it returns immediately | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<body> | |
<div><button id="bt">Run action</button> <span id="spn1"></span> <span id="spn2"></span></div> | |
<script> | |
document.getElementById('bt').addEventListener('click', async () => { | |
console.log('Starting action one'); | |
const serverRes1 = await fetch('http://localhost:8090/test?action=one').then(res => res.json()); | |
document.getElementById('spn1').innerText = serverRes1.content; | |
console.log('Starting action two'); | |
const serverRes2 = await fetch('http://localhost:8090/test?action=two').then(res => res.json()); | |
document.getElementById('spn2').innerText = serverRes2.content; | |
console.log('Done with actions'); | |
}); | |
</script> | |
<script src="fetch-override.js"></script><!-- TODO: only insert during tests - like in build --> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Starting action one | |
New fetch - now: 1 | |
New json - now: 2 | |
Done handling fetch - now: 1 | |
Starting action two | |
New fetch - now: 2 | |
Done handling json - now: 1 | |
New json - now: 2 | |
Done handling fetch - now: 1 | |
Done with actions | |
Done handling json - now: 0 | |
Done with backend - firing our promise |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment