Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6119c88
Web Platform Tests for HTML video element lazy loading via the loadin…
scottjehl Jan 7, 2026
0b1b671
rename files to use a .tentative.html convention since they correspon…
scottjehl Jan 8, 2026
bb70f86
Merge pull request #2 from Squarespace/scottjehl/tentative-rename
scottjehl Jan 8, 2026
958e09b
normalize the https links to squarespace and titles
scottjehl Jan 14, 2026
12e5e03
omit rel help links to tentative urls
scottjehl Jan 14, 2026
2c8ab51
one more https link:
scottjehl Jan 14, 2026
93197db
increase distance for below-viewport to 1000vh to tolerate varying im…
scottjehl Jan 14, 2026
75414dc
loadstart firing should fail this test as well
scottjehl Jan 14, 2026
b463e44
add 2s trickle to video request and compare loadeddata to onload in test
scottjehl Jan 14, 2026
c285504
changed test to rely less on timing and more on events. used the play…
scottjehl Jan 14, 2026
53a9a24
let test harness timeout on its own if event never fires
scottjehl Jan 14, 2026
772ebf5
modified this test to check that an in-viewport lazy video's loadstar…
scottjehl Jan 14, 2026
95fdaae
adjust test to compare the size of the video element before and after…
scottjehl Jan 14, 2026
136a26b
add trickle 2s to reduce flaky
scottjehl Jan 14, 2026
a302230
simplify test to compare onload to video load completeness
scottjehl Jan 14, 2026
e2e728c
t.step unnecessary here
scottjehl Jan 14, 2026
aafc795
fix lint errors
scottjehl Jan 14, 2026
8460c52
changed this test to use the width and height proxy instead as it see…
scottjehl Jan 15, 2026
3b761e0
simplify this test to look for eager to load data regardless of location
scottjehl Jan 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<link rel="author" title="Squarespace" href="https://www.squarespace.com/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<video id="target"></video>

<script>
const video = document.getElementById("target");

test(() => {
assert_equals(
video.loading,
"eager",
"Video loading property should be 'eager' when attribute is not set"
);
}, "Video loading attribute default should be eager when not set");
</script>

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<link rel="author" title="Squarespace" href="https://www.squarespace.com/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<video id="target" loading="lazy"></video>

<script>
const video = document.getElementById("target");

test(() => {
assert_equals(
video.loading,
"lazy",
"Video loading attribute should be 'lazy' when set to 'lazy'"
);
}, "Video loading attribute should reflect the loading content attribute");
</script>

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<link rel="author" title="Squarespace" href="https://www.squarespace.com/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<video id="target"></video>

<script>
const video = document.getElementById("target");

test(() => {
video.removeAttribute("loading");
assert_equals(video.loading, "eager", "Default value should be 'eager'");
}, "Video loading attribute default value is 'eager'");

test(() => {
video.setAttribute("loading", "lazy");
assert_equals(video.loading, "lazy", "loading='lazy' should reflect as 'lazy'");
}, "Video loading attribute reflects 'lazy'");

test(() => {
video.setAttribute("loading", "eager");
assert_equals(video.loading, "eager", "loading='eager' should reflect as 'eager'");
}, "Video loading attribute reflects 'eager'");

test(() => {
video.setAttribute("loading", "invalid");
assert_equals(video.loading, "eager", "Invalid value should reflect as 'eager'");
}, "Video loading attribute with invalid value reflects as 'eager'");

test(() => {
video.setAttribute("loading", "");
assert_equals(video.loading, "eager", "Empty string should reflect as 'eager'");
}, "Video loading attribute with empty string reflects as 'eager'");

test(() => {
video.setAttribute("loading", "LAZY");
assert_equals(video.loading, "lazy", "Uppercase 'LAZY' should reflect as 'lazy'");
}, "Video loading attribute is case-insensitive for 'lazy'");

test(() => {
video.setAttribute("loading", "EAGER");
assert_equals(video.loading, "eager", "Uppercase 'EAGER' should reflect as 'eager'");
}, "Video loading attribute is case-insensitive for 'eager'");

test(() => {
video.loading = "lazy";
assert_equals(video.getAttribute("loading"), "lazy", "Setting IDL to 'lazy' should set content attribute");
assert_equals(video.loading, "lazy", "IDL should reflect 'lazy'");
}, "Setting video.loading IDL attribute to 'lazy'");

test(() => {
video.loading = "eager";
assert_equals(video.getAttribute("loading"), "eager", "Setting IDL to 'eager' should set content attribute");
assert_equals(video.loading, "eager", "IDL should reflect 'eager'");
}, "Setting video.loading IDL attribute to 'eager'");

test(() => {
video.loading = "invalid";
assert_equals(video.getAttribute("loading"), "invalid", "Setting IDL to 'invalid' should set content attribute");
assert_equals(video.loading, "eager", "IDL should reflect 'eager' for invalid content attribute");
}, "Setting video.loading IDL attribute to invalid value");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<link rel="author" title="Squarespace" href="https://www.squarespace.com/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<style>
#target {
position: absolute;
top: 1000vh;
}
</style>

<video id="target" src="/media/A4.mp4" loading="lazy" muted playsinline autoplay></video>

<script>
const t = async_test(
"Video with loading=lazy and autoplay that is not visible in viewport does not load video data or autoplay"
);

const video = document.getElementById("target");

video.addEventListener("loadstart", () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the video element in markup and adding a listener in a script afterwards can miss the event. Either create the video in script or have a capturing listener before the video element exists.

(Same in some other tests.)

t.step(() => {
assert_unreached("Video should not fire loadstart event when not visible in viewport");
});
});

t.step_timeout(() => {
assert_equals(video.readyState, HTMLMediaElement.HAVE_NOTHING);
assert_true(video.paused, "Video should not autoplay when not visible in viewport");
t.done();
}, 2000);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<link rel="author" title="Squarespace" href="https://www.squarespace.com/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<style>
.below-viewport {
position: absolute;
top: 1000vh;
}
</style>

<video id="below_viewport" class="below-viewport" src="/media/A4.mp4?pipe=trickle(d2)" loading="eager"></video>

<script>
const belowViewportVideo = document.getElementById("below_viewport");
let dataLoaded = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not used; remove.


async_test(t => {
belowViewportVideo.addEventListener("loadeddata", t.step_func(() => {
dataLoaded = true;
t.done();
}));

//if data is not loaded after 5 seconds, assert unreached
t.step_timeout(() => {
assert_unreached("Eager video should load data immediately");
}, 5000);
}, "Videos with loading='eager' load immediately regardless of viewport position");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<link rel="author" title="Squarespace" href="https://www.squarespace.com/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<style>
#target {
position: absolute;
top: 1000vh;
}
</style>

<video id="target" src="/media/A4.mp4" loading="lazy" muted playsinline autoplay width="100" height="100"></video>

<script>
const video = document.getElementById("target");

const t = async_test(
"Video with loading=lazy and autoplay does not play while not visible, then plays once scrolled into view"
);
let scrolledIntoView = false;

t.step_timeout(() => {
// Verify video has not played before scrolling
t.step(() => {
assert_true(video.paused, "Video should be paused when not visible in viewport");
});

// bind to play event to assert true that play has begun after scroll has been called
video.addEventListener("play", () => {
t.step(() => {
assert_true(scrolledIntoView, "Video should fire play event after scrolling into view");
});
t.done();
});

// scroll into view and set scrolledIntoView to true
step_timeout(() => {
scrolledIntoView = true;
video.scrollIntoView();
}, 1000);
}, 2000);


</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<link rel="author" title="Squarespace" href="https://www.squarespace.com/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<video id="target" src="/media/A4.mp4?pipe=trickle(d2)" loading="lazy"></video>

<script>
const video = document.getElementById("target");

let windowLoadFired = false;

async_test(t => {
window.addEventListener("load", t.step_func(() => {
windowLoadFired = true;
}));

video.addEventListener("loadstart", t.step_func(() => {
assert_true(windowLoadFired, "Window load event should fire before loadstart for lazy video in viewport");
t.done();
}));

}, "In-viewport video with loading='lazy' fires window load before loadstart");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<link rel="author" title="Squarespace" href="https://www.squarespace.com/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<style>
#target {
position: absolute;
top: 1000vh;
}
</style>

<video id="target" controls src="/media/A4.mp4" loading="lazy"></video>

<script>
const video = document.getElementById("target");

const t = async_test(
"Video with loading=lazy does not fetch video while not visible, then fetches video once scrolled into view"
);

// Step 1: Not visible => check initial dimensions.
t.step_timeout(() => {
t.step(() => {
const initialWidth = video.offsetWidth;
const initialHeight = video.offsetHeight;

// Step 2: Scroll into view => video should load and dimensions should change.
video.scrollIntoView();

t.step_timeout(() => {
t.step(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

const finalWidth = video.offsetWidth;
const finalHeight = video.offsetHeight;
assert_not_equals(
finalWidth,
initialWidth,
"Video width should change after video loads"
);
assert_not_equals(
finalHeight,
initialHeight,
"Video height should change after video loads"
);
});
t.done();
}, 2000);
});
}, 2000);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<link rel="author" title="Squarespace" href="https://www.squarespace.com/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<style>
#target {
position: absolute;
top: 1000vh;
}
</style>

<video id="target" poster="/media/2048x1360-random.jpg" loading="lazy"></video>

<script>
const video = document.getElementById("target");

const t = async_test(
"Video with loading=lazy does not fetch poster while not visible, then fetches poster once scrolled into view"
);

// Step 1: Not visible => check initial dimensions.
t.step_timeout(() => {
t.step(() => {
const initialWidth = video.offsetWidth;
const initialHeight = video.offsetHeight;

// Step 2: Scroll into view => poster should load and dimensions should change.
video.scrollIntoView();

t.step_timeout(() => {
t.step(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

const finalWidth = video.offsetWidth;
const finalHeight = video.offsetHeight;
assert_not_equals(
finalWidth,
initialWidth,
"Video width should change after poster loads"
);
assert_not_equals(
finalHeight,
initialHeight,
"Video height should change after poster loads"
);
});
t.done();
}, 2000);
});
}, 2000);
</script>
</body>
</html>
Loading