Skip to content

Commit

Permalink
Fix svg graphics element tests
Browse files Browse the repository at this point in the history
  • Loading branch information
johannesodland committed Feb 4, 2024
1 parent 8b52a5c commit 1bfe1d9
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 49 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ jobs:
uses: actions/configure-pages@v3
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Build
run: "npm run build"
- name: Test build
run: "npm run build -- --mode test"
- name: Checkout WPT
run: "npm run test-setup"
- name: WPT hosts
Expand All @@ -57,6 +57,8 @@ jobs:
run: "npm run test:compare"
- name: Test results summary
run: echo "Passed $(grep -c '^PASS' ./test/report/summary.txt) of $(grep -c '^' ./test/report/summary.txt) tests" >> $GITHUB_STEP_SUMMARY
- name: Build
run: "npm run build"
- name: Clean build files
run: "rm -rf node_modules test/wpt"
- name: Upload artifact
Expand Down
64 changes: 58 additions & 6 deletions src/scroll-timeline-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export function measureSource (source) {

/**
* Measure subject element relative to source
* @param {HTMLElement} source
* @param {HTMLElement|SVGElement} source
* @param {HTMLElement|undefined} subject
* @param subject
*/
Expand All @@ -201,18 +201,70 @@ export function measureSubject(source, subject) {
let node = subject;
const ancestor = source.offsetParent;
while (node && node != ancestor) {
left += node.offsetLeft;
top += node.offsetTop;
node = node.offsetParent;
if (node instanceof SVGElement) {
let rootSvgElement = node.ownerSVGElement;
while (rootSvgElement.ownerSVGElement)
rootSvgElement = rootSvgElement.ownerSVGElement;

const matrix = node.getCTM();
const bbox = node.getBBox();
let point = rootSvgElement.createSVGPoint();
point.x = bbox.x;
point.y = bbox.y;

// Position relative to svg-element
const nodePosition = point.matrixTransform(matrix);
const svgParent = rootSvgElement.parentElement;
const svgLeft = rootSvgElement.getBoundingClientRect().left - svgParent.getBoundingClientRect().left;
const svgTop = rootSvgElement.getBoundingClientRect().top - svgParent.getBoundingClientRect().top;
left += svgLeft + nodePosition.x;
top += svgTop + nodePosition.y;
node = svgParent;
} else {
left += node.offsetLeft;
top += node.offsetTop;
if (!node.offsetParent) {
// The top level element (body) does not have an offsetParent, and no offsetTop/Left
// If the body has margins, they need to be added
const style = getComputedStyle(node);
top += parseInt(style.marginTop);
left += parseInt(style.marginLeft);
}
node = node.offsetParent;
}
}
left -= source.offsetLeft + source.clientLeft;
top -= source.offsetTop + source.clientTop;

let offsetWidth, offsetHeight
if (subject instanceof SVGElement) {
let rootSvgElement = subject.ownerSVGElement;
while (rootSvgElement.ownerSVGElement)
rootSvgElement = rootSvgElement.ownerSVGElement;

const matrix = subject.getCTM();
const bbox = subject.getBBox();
let topLeftPoint = rootSvgElement.createSVGPoint();
topLeftPoint.x = bbox.x;
topLeftPoint.y = bbox.y;
let bottomRightPoint = rootSvgElement.createSVGPoint();
bottomRightPoint.x = bbox.x + bbox.width;
bottomRightPoint.y = bbox.y + bbox.height;

const tlPosition = topLeftPoint.matrixTransform(matrix);
const brPosition = bottomRightPoint.matrixTransform(matrix);
offsetWidth = Math.abs(brPosition.x - tlPosition.x);
offsetHeight = Math.abs(brPosition.y - tlPosition.y);
} else {
offsetWidth = subject.offsetWidth;
offsetHeight = subject.offsetHeight;
}
const style = getComputedStyle(subject);
return {
top,
left,
offsetWidth: subject.offsetWidth,
offsetHeight: subject.offsetHeight,
offsetWidth,
offsetHeight,
fontSize: style.fontSize,
};
}
Expand Down
14 changes: 6 additions & 8 deletions src/scroll-timeline-css-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,22 +248,20 @@ export class StyleParser {
// Add 1s as duration to fix this.
if(hasAnimationTimeline) {
if(!this.hasDuration(shorthand)) {

let previousShorthand = shorthand
// `auto` also is valid duration. Older browsers can’t always
// handle it properly, so we remove it from the shorthand.
if (this.hasAutoDuration(shorthand)) {
rule.block.contents = rule.block.contents.replace(
'auto',
' '
);
shorthand = previousShorthand.replace('auto', ' ')
rule.block.contents = rule.block.contents.replace(previousShorthand, shorthand);
previousShorthand = shorthand
}

// TODO: Should keep track of whether duration is artificial or not,
// so that we can later track that we need to update timing to
// properly see duration as 'auto' for the polyfill.
rule.block.contents = rule.block.contents.replace(
shorthand, " 1s " + shorthand
);
shorthand = " 1s " + previousShorthand
rule.block.contents = rule.block.contents.replace(previousShorthand, shorthand);
shouldReplacePart = true;
}
}
Expand Down
37 changes: 37 additions & 0 deletions test/entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import '../src/index.js'
// The polyfill is dependent on the animationstart event for polyfilling animations declared in css.
// This causes timing issues when running some wpt tests that wait for animation.ready.
// The tests might run before the animations are polyfilled, making them flaky.

// The following code delays a selected list of tests until animations are polyfilled.

// List of names of tests that should wait for animationstart
const cssAnimationTests = [
'View timeline attached to SVG graphics element'
]

const animationsStarted = new Promise((resolve) => {
window.addEventListener('animationstart', () => {
setTimeout(() => resolve(), 1);
});
})

// Proxy the promise_test function
let nativePromiseTest;
Reflect.defineProperty(window, 'promise_test', {
get() {
return (func, name, properties) => {
if (cssAnimationTests.includes(name)) {
// Wait for animationstart before starting tests
return nativePromiseTest.call(null, async (...args) => {
await animationsStarted;
return func.call(null, ...args);
}, name, properties);
} else {
nativePromiseTest.call(null, func, name, properties);
}
};
}, set(v) {
nativePromiseTest = v;
}
});
20 changes: 10 additions & 10 deletions test/expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ FAIL /scroll-animations/css/animation-timeline-computed.html Property animation-
FAIL /scroll-animations/css/animation-timeline-computed.html Property animation-timeline value 'view(1px y)'
FAIL /scroll-animations/css/animation-timeline-computed.html Property animation-timeline value 'view(y auto)'
FAIL /scroll-animations/css/animation-timeline-computed.html Property animation-timeline value 'view(y auto auto)'
FAIL /scroll-animations/css/animation-timeline-deferred.html Animation.timeline returns attached timeline
PASS /scroll-animations/css/animation-timeline-deferred.html Animation.timeline returns attached timeline
FAIL /scroll-animations/css/animation-timeline-deferred.html Animation.timeline returns null for inactive deferred timeline
FAIL /scroll-animations/css/animation-timeline-deferred.html Animation.timeline returns null for inactive (overattached) deferred timeline
FAIL /scroll-animations/css/animation-timeline-ignored.tentative.html Changing animation-timeline changes the timeline (sanity check)
Expand Down Expand Up @@ -307,7 +307,7 @@ FAIL /scroll-animations/css/scroll-timeline-name-shadow.html Outer animation can
FAIL /scroll-animations/css/scroll-timeline-name-shadow.html Inner animation can see scroll timeline defined by ::part
FAIL /scroll-animations/css/scroll-timeline-name-shadow.html Slotted element can see scroll timeline within the shadow
PASS /scroll-animations/css/scroll-timeline-nearest-dirty.html Unrelated style mutation does not affect anonymous timeline
FAIL /scroll-animations/css/scroll-timeline-nearest-with-absolute-positioned-element.html Resolving scroll(nearest) for an absolutely positioned element
PASS /scroll-animations/css/scroll-timeline-nearest-with-absolute-positioned-element.html Resolving scroll(nearest) for an absolutely positioned element
FAIL /scroll-animations/css/scroll-timeline-paused-animations.html Test that the scroll animation is paused
FAIL /scroll-animations/css/scroll-timeline-paused-animations.html Test that the scroll animation is paused by updating animation-play-state
FAIL /scroll-animations/css/scroll-timeline-range-animation.html Animation with ranges [initial, initial]
Expand Down Expand Up @@ -405,15 +405,15 @@ PASS /scroll-animations/css/timeline-scope-parsing.tentative.html e.style['timel
PASS /scroll-animations/css/timeline-scope-parsing.tentative.html e.style['timeline-scope'] = "rgb(1, 2, 3)" should not set the property value
PASS /scroll-animations/css/timeline-scope-parsing.tentative.html e.style['timeline-scope'] = "#fefefe" should not set the property value
FAIL /scroll-animations/css/timeline-scope.html Descendant can attach to deferred timeline
PASS /scroll-animations/css/timeline-scope.html Deferred timeline with no attachments
FAIL /scroll-animations/css/timeline-scope.html Inner timeline does not interfere with outer timeline
PASS /scroll-animations/css/timeline-scope.html Deferred timeline with two attachments
FAIL /scroll-animations/css/timeline-scope.html Deferred timeline with no attachments
PASS /scroll-animations/css/timeline-scope.html Inner timeline does not interfere with outer timeline
FAIL /scroll-animations/css/timeline-scope.html Deferred timeline with two attachments
FAIL /scroll-animations/css/timeline-scope.html Dynamically re-attaching
FAIL /scroll-animations/css/timeline-scope.html Dynamically detaching
FAIL /scroll-animations/css/timeline-scope.html Removing/inserting element with attaching timeline
FAIL /scroll-animations/css/timeline-scope.html Ancestor attached element becoming display:none/block
FAIL /scroll-animations/css/timeline-scope.html A deferred timeline appearing dynamically in the ancestor chain
FAIL /scroll-animations/css/timeline-scope.html Animations prefer non-deferred timelines
PASS /scroll-animations/css/timeline-scope.html Animations prefer non-deferred timelines
FAIL /scroll-animations/css/view-timeline-animation-range-update.tentative.html Ensure that animation is updated on a style change
FAIL /scroll-animations/css/view-timeline-animation.html Default view-timeline
FAIL /scroll-animations/css/view-timeline-animation.html Horizontal view-timeline
Expand Down Expand Up @@ -1032,7 +1032,7 @@ FAIL /scroll-animations/view-timelines/get-keyframes-with-timeline-offset.html C
FAIL /scroll-animations/view-timelines/get-keyframes-with-timeline-offset.html Retain specified ordering of keyframes with timeline offsets
FAIL /scroll-animations/view-timelines/get-keyframes-with-timeline-offset.html Include unreachable keyframes
FAIL /scroll-animations/view-timelines/get-keyframes-with-timeline-offset.html Mix of computed and timeline offsets.
FAIL /scroll-animations/view-timelines/inline-subject.html View timeline attached to SVG graphics element
TIMEOUT /scroll-animations/view-timelines/inline-subject.html View timeline attached to SVG graphics element
FAIL /scroll-animations/view-timelines/inline-view-timeline-current-time.tentative.html View timeline with start and end scroll offsets that do not align with the scroll boundaries
FAIL /scroll-animations/view-timelines/inline-view-timeline-current-time.tentative.html View timeline does not clamp starting scroll offset at 0
PASS /scroll-animations/view-timelines/inline-view-timeline-current-time.tentative.html View timeline does not clamp end scroll offset at max scroll
Expand All @@ -1044,9 +1044,9 @@ FAIL /scroll-animations/view-timelines/sticky/view-timeline-sticky-offscreen-4.h
FAIL /scroll-animations/view-timelines/sticky/view-timeline-sticky-offscreen-5.html View timeline bottom-sticky before entry and top-sticky after exit.
FAIL /scroll-animations/view-timelines/sticky/view-timeline-sticky-offscreen-6.html View timeline target > viewport, bottom-sticky during entry and top-sticky during exit.
FAIL /scroll-animations/view-timelines/sticky/view-timeline-sticky-offscreen-7.html View timeline target > viewport, bottom-sticky and top-sticky during contain.
FAIL /scroll-animations/view-timelines/svg-graphics-element-001.html View timeline attached to SVG graphics element
FAIL /scroll-animations/view-timelines/svg-graphics-element-002.html View timeline attached to SVG graphics element
FAIL /scroll-animations/view-timelines/svg-graphics-element-003.html View timeline attached to SVG graphics element
PASS /scroll-animations/view-timelines/svg-graphics-element-001.html View timeline attached to SVG graphics element
PASS /scroll-animations/view-timelines/svg-graphics-element-002.html View timeline attached to SVG graphics element
PASS /scroll-animations/view-timelines/svg-graphics-element-003.html View timeline attached to SVG graphics element
FAIL /scroll-animations/view-timelines/timeline-offset-in-keyframe.html Timeline offsets in programmatic keyframes
FAIL /scroll-animations/view-timelines/timeline-offset-in-keyframe.html String offsets in programmatic keyframes
PASS /scroll-animations/view-timelines/timeline-offset-in-keyframe.html Invalid timeline offset in programmatic keyframe throws
Expand Down
47 changes: 24 additions & 23 deletions vite.config.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import { resolve } from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
build: {
sourcemap: true,
lib: {
// Could also be a dictionary or array of multiple entry points
entry: resolve(__dirname, 'src/index.js'),
name: 'ScrollTimeline',
// the proper extensions will be added
fileName: (format, entryAlias) => `scroll-timeline${format=='iife'?'':'-' + format}.js`,
formats: ['iife'],
},
minify: 'terser',
terserOptions: {
keep_classnames: /^((View|Scroll)Timeline)|CSS.*$/
},
rollupOptions: {
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
},
export default defineConfig(({ mode }) => {
return {
build: {
sourcemap: true,
lib: {
// Could also be a dictionary or array of multiple entry points
entry: resolve(__dirname, mode === 'test' ? 'test/entry.js' : 'src/index.js'),
name: 'ScrollTimeline',
// the proper extensions will be added
fileName: (format, entryAlias) => `scroll-timeline${format == 'iife' ? '' : '-' + format}.js`,
formats: ['iife'],
},
}
},
minify: 'terser',
terserOptions: {
keep_classnames: /^((View|Scroll)Timeline)|CSS.*$/
},
rollupOptions: {
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {},
},
}
},
};
})

0 comments on commit 1bfe1d9

Please sign in to comment.