Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

  • This WPT test may be referenced by the following Test IDs:
<!DOCTYPE html>
<meta charset="utf-8">
<title>performance.getSpeculations() - navigations from speculation rules</title>
<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="/speculation-rules/resources/utils.js"></script>
<script src="/speculation-rules/prefetch/resources/utils.sub.js"></script>
<script src="support/speculation-measurement-utils.js"></script>
<body>
<script>
setup(() => {
assertSpeculationRulesIsSupported();
assert_true(isSpeculationMeasurementEnabled(),
"SpeculationMeasurement feature must be enabled");
});
promise_test(async t => {
// Before any speculation rules, navigations should be empty.
const data = performance.getSpeculations();
assert_true(Array.isArray(data.navigations),
"navigations should be an array");
assert_equals(data.navigations.length, 0,
"navigations should be empty when no speculation rules exist");
}, "Empty navigations when no speculation rules");
promise_test(async t => {
// Verify the SpeculationData interface shape includes navigations.
const data = performance.getSpeculations();
assert_true('preloads' in data, "should have preloads");
assert_true('navigations' in data, "should have navigations");
assert_true(Array.isArray(data.preloads), "preloads should be an array");
assert_true(Array.isArray(data.navigations),
"navigations should be an array");
}, "SpeculationData has both preloads and navigations");
promise_test(async t => {
// Add a prefetch speculation rule and verify it appears in navigations.
const prefetchUrl = new URL('/common/blank.html?prefetch_test',
location.href).href;
insertSpeculationRules({
prefetch: [{
source: 'list',
urls: [prefetchUrl],
eagerness: 'eager',
tag: 'test-prefetch'
}]
});
// Wait for the speculation rules to be processed.
await new Promise(resolve => t.step_timeout(resolve, 100));
const data = performance.getSpeculations();
const nav = data.navigations.find(n => n.url === prefetchUrl);
assert_not_equals(nav, undefined,
"Prefetched URL should appear in navigations");
assert_equals(nav.type, "prefetch", "type should be 'prefetch'");
assert_equals(nav.url, prefetchUrl, "url should match");
assert_equals(nav.eagerness, "eager", "eagerness should be 'eager'");
}, "Prefetch speculation rule appears in navigations");
promise_test(async t => {
// Test that tags are exposed when set.
const prefetchUrl = new URL('/common/blank.html?tags_test',
location.href).href;
insertSpeculationRules({
prefetch: [{
source: 'list',
urls: [prefetchUrl],
eagerness: 'immediate',
tag: 'my-tag'
}]
});
await new Promise(resolve => t.step_timeout(resolve, 100));
const data = performance.getSpeculations();
const nav = data.navigations.find(n => n.url === prefetchUrl);
assert_not_equals(nav, undefined, "Should find the navigation entry");
assert_true(Array.isArray(nav.tags), "tags should be an array");
assert_true(nav.tags.includes('my-tag'),
"tags should include the specified tag");
}, "Navigation tags are exposed");
promise_test(async t => {
// Test that navigations without tags have null tags.
const prefetchUrl = new URL('/common/blank.html?no_tags_test',
location.href).href;
insertSpeculationRules({
prefetch: [{
source: 'list',
urls: [prefetchUrl]
}]
});
await new Promise(resolve => t.step_timeout(resolve, 100));
const data = performance.getSpeculations();
const nav = data.navigations.find(n => n.url === prefetchUrl);
assert_not_equals(nav, undefined, "Should find the navigation entry");
assert_equals(nav.tags, null,
"tags should be null when not specified");
}, "Navigation without tags has null tags");
promise_test(async t => {
// Verify SpeculationNavigationData interface exists.
assert_true('SpeculationNavigationData' in window,
"SpeculationNavigationData should exist as a global interface");
}, "SpeculationNavigationData interface exists");
promise_test(async t => {
// Add a speculation rule, then remove it, and verify the navigation
// entry persists (sent candidates are accumulated and never removed).
const prefetchUrl = new URL('/common/blank.html?remove_test',
location.href).href;
const script = document.createElement('script');
script.type = 'speculationrules';
script.textContent = JSON.stringify({
prefetch: [{
urls: [prefetchUrl],
eagerness: 'eager',
tag: 'to-remove'
}]
});
document.head.appendChild(script);
await new Promise(resolve => t.step_timeout(resolve, 100));
let data = performance.getSpeculations();
let nav = data.navigations.find(n => n.url === prefetchUrl);
assert_not_equals(nav, undefined,
"Navigation should exist before removal");
// Remove the speculation rules script element.
script.remove();
await new Promise(resolve => t.step_timeout(resolve, 100));
data = performance.getSpeculations();
nav = data.navigations.find(n => n.url === prefetchUrl);
assert_not_equals(nav, undefined,
"Navigation should persist after removing the speculation rules");
}, "Navigations persist after speculation rules are removed");
promise_test(async t => {
// Add a speculation rule, then replace it with a modified version.
// Both the original and updated navigation entries should be present,
// since sent candidates are accumulated and never removed.
const prefetchUrl = new URL('/common/blank.html?modify_test',
location.href).href;
const script = document.createElement('script');
script.type = 'speculationrules';
script.textContent = JSON.stringify({
prefetch: [{
urls: [prefetchUrl],
eagerness: 'eager',
tag: 'original-tag'
}]
});
document.head.appendChild(script);
await new Promise(resolve => t.step_timeout(resolve, 100));
let data = performance.getSpeculations();
let nav = data.navigations.find(
n => n.url === prefetchUrl && n.tags && n.tags.includes('original-tag'));
assert_not_equals(nav, undefined,
"Navigation with original tag should exist");
// Remove the original and add a new rule with a different tag.
script.remove();
const script2 = document.createElement('script');
script2.type = 'speculationrules';
script2.textContent = JSON.stringify({
prefetch: [{
urls: [prefetchUrl],
eagerness: 'moderate',
tag: 'updated-tag'
}]
});
document.head.appendChild(script2);
t.add_cleanup(() => script2.remove());
await new Promise(resolve => t.step_timeout(resolve, 100));
data = performance.getSpeculations();
nav = data.navigations.find(
n => n.url === prefetchUrl && n.tags && n.tags.includes('original-tag'));
assert_not_equals(nav, undefined,
"Navigation with original tag should persist after modification");
nav = data.navigations.find(
n => n.url === prefetchUrl && n.tags && n.tags.includes('updated-tag'));
assert_not_equals(nav, undefined,
"Navigation with updated tag should also exist");
}, "Navigations persist and accumulate after speculation rules are modified");
</script>
</body>