| <!DOCTYPE html> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/common/dispatcher/dispatcher.js"></script> |
| <script src="/common/utils.js"></script> |
| <script src="../resources/utils.js"></script> |
| <script src="resources/utils.sub.js"></script> |
| <script src="/common/subset-tests-by-key.js"></script> |
| |
| <meta name="variant" content="?include=noPrefetch"> |
| <meta name="variant" content="?include=afterResponse"> |
| <meta name="variant" content="?include=waitingForResponse"> |
| <meta name="variant" content="?include=waitingForRedirect"> |
| |
| <script> |
| const setupProperties = {}; |
| if (shouldRunSubTest('waitingForResponse') || shouldRunSubTest('waitingForRedirect')) { |
| // These tests rely on the relationship between these timeouts, the delays |
| // on the server, and any vendor specific timeouts. Force a multiplier of 1, |
| // since using setTimeout directly incurs the wrath of `wpt lint`. |
| setupProperties.timeout_multiplier = 1; |
| } |
| setup(() => assertSpeculationRulesIsSupported(), setupProperties); |
| |
| subsetTestByKey('noPrefetch', promise_test, async t => { |
| const agent = await spawnWindow(t); |
| const landingUrl = agent.getExecutorURL({page: 2}); |
| |
| await agent.navigate(landingUrl); |
| assert_not_prefetched(await agent.getRequestHeaders(), `${landingUrl} should not be prefetched.`); |
| |
| // It's generally expected that events occur in this order: |
| // ... -> connectEnd --> requestStart --> responseStart --> ... |
| const [entry] = await agent.execute_script( |
| () => performance.getEntriesByType('navigation')); |
| assert_less_than_equal(entry.connectEnd, entry.requestStart); |
| assert_less_than_equal(entry.requestStart, entry.responseStart); |
| }, "PerformanceNavigationTiming data should be in the correct order (no prefetch)"); |
| |
| subsetTestByKey('afterResponse', promise_test, async t => { |
| const agent = await spawnWindow(t); |
| const landingUrl = agent.getExecutorURL({page: 2}); |
| await agent.forceSinglePrefetch(landingUrl); |
| |
| await agent.navigate(landingUrl); |
| assert_prefetched(await agent.getRequestHeaders(), `${landingUrl} should have been prefetched.`); |
| |
| // Since the response should have started before the navigation, these should |
| // all have the same value (i.e., the start of the fetch). |
| const [entry] = await agent.execute_script( |
| () => performance.getEntriesByType('navigation')); |
| assert_equals(entry.connectEnd, entry.requestStart); |
| assert_equals(entry.requestStart, entry.responseStart); |
| }, "PerformanceNavigationTiming data should show 'instantaneous' events completed beforehand"); |
| |
| subsetTestByKey('waitingForResponse', promise_test, async t => { |
| const agent = await spawnWindow(t); |
| const landingUrl = agent.getExecutorURL({executor: 'slow-executor.py', delay: '3', page: 2}); |
| await agent.forceSinglePrefetch(landingUrl, {}, /*wait_for_completion=*/false); |
| |
| // Chromium, at least, will give up the prefetch if the response head doesn't |
| // come back within 1 second of navigation. So since the server will take |
| // 3 seconds to respond, we wait 2.5 seconds before navigating, to ensure the |
| // response comes back within about 0.5 seconds. |
| await new Promise(resolve => t.step_timeout(resolve, 2_500)); |
| |
| await agent.navigate(landingUrl); |
| assert_prefetched(await agent.getRequestHeaders(), `${landingUrl} should have been prefetched.`); |
| |
| // Unlike the `afterResponse` test, we expect delays between `requestStart` |
| // and `responseStart` here. If our timing was perfect and server round-trips |
| // were instantaneous, that delay would be 0.5 seconds. To give ourselves a |
| // bit of wiggle room, instead we assert that it's at least 0.1 seconds. |
| const [entry] = await agent.execute_script( |
| () => performance.getEntriesByType('navigation')); |
| assert_less_than_equal(entry.connectEnd, entry.requestStart, "connectEnd must be before requestStart"); |
| assert_greater_than(entry.responseStart, entry.requestStart + 100, "responseStart must be > 100 ms after requestStart"); |
| }, "PerformanceNavigationTiming data should show noticeable TTFB if the response is slow"); |
| |
| subsetTestByKey('waitingForRedirect', promise_test, async t => { |
| const agent = await spawnWindow(t); |
| const landingUrl = agent.getExecutorURL({page: 2}); |
| const slowRedirectUrl = new URL(`/common/slow-redirect.py?delay=3&location=${encodeURIComponent(landingUrl)}`, document.baseURI); |
| await agent.forceSinglePrefetch(slowRedirectUrl, {}, /*wait_for_completion=*/false); |
| |
| // Considerations here are the same as for `waitingForResponse`. |
| await new Promise(resolve => t.step_timeout(resolve, 2_500)); |
| |
| await agent.navigate(slowRedirectUrl, {expectedDestinationUrl: landingUrl}); |
| assert_prefetched(await agent.getRequestHeaders(), `${landingUrl} should have been prefetched.`); |
| |
| // As in `waitingForResponse`, we expect at least 0.1 seconds TTFB. Unlike in |
| // that test, the delta isn't captured by `requestStart` vs. `responseStart`, |
| // because these stats only measure the final request/response pair, which is |
| // not delayed. |
| const [entry] = await agent.execute_script( |
| () => performance.getEntriesByType('navigation')); |
| assert_less_than_equal(entry.connectEnd, entry.requestStart, "connectEnd must be before requestStart"); |
| assert_less_than_equal(entry.requestStart, entry.responseStart, "requestStart must be before responseStart"); |
| assert_greater_than(entry.connectEnd, 100, "connectEnd must be > 100 ms"); |
| }, "PerformanceNavigationTiming data should show noticeable TTFB if the response is slow"); |
| </script> |