Skip to content

Commit cb58ca0

Browse files
committed
add playwright-based e2e tests with visual regression testing, add a snapshot test of the 03_graphs example for starters, updating CSS to use a local font to guarantee the same font across platforms (f.e. local vs CI) for consistent screenshots. Update tsconfig to ignore compiling TS files in e2e/, playwright handles those directly
1 parent 61d018f commit cb58ca0

14 files changed

Lines changed: 273 additions & 31 deletions

File tree

‎.github/workflows/tests.yml‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,34 @@ jobs:
1414
node-version: [lts/*]
1515

1616
steps:
17-
- uses: actions/checkout@v2
17+
- uses: actions/checkout@v4
1818
- name: Use Node.js ${{ matrix.node-version }}
1919
uses: actions/setup-node@v4
2020
with:
2121
node-version: ${{ matrix.node-version }}
22+
cache: npm
2223
- run: npm clean-install
24+
- run: npx playwright install --with-deps chromium
25+
26+
# Uncomment this if for some reason you suspect CI (Linux) build differes from local, then you can inspect output.
27+
# - name: Upload dist artifact
28+
# uses: actions/upload-artifact@v4
29+
# with:
30+
# name: dist
31+
# path: dist/
32+
2333
- run: npm test
34+
35+
- name: Upload Playwright rest results
36+
uses: actions/upload-artifact@v4
37+
if: always()
38+
with:
39+
name: playwright-test-results
40+
path: test-results/
41+
42+
- name: Upload Playwright e2e snapshots
43+
uses: actions/upload-artifact@v4
44+
if: always()
45+
with:
46+
name: playwright-e2e-snapshots
47+
path: e2e/**/*-snapshots/

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
build/npm*
66
node_modules
77
npm-debug.log
8+
test-results/

‎assets/style.css‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1+
@font-face {
2+
font-family: 'Allerta';
3+
font-style: normal;
4+
font-weight: 400;
5+
font-display: swap;
6+
src: url("../examples/font/Allerta/Allerta-Regular.woff2") format('woff2');
7+
8+
/* No idea what this is, copied from Google Fonts embed code */
9+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
10+
}
11+
112
body {
213
background: #fff;
3-
font-family: Helvetica, Arial, sans;
14+
font-family: Allerta, Helvetica, Arial, sans-serif;
15+
letter-spacing: -0.03em;
416
}
517

618
a {
@@ -22,7 +34,7 @@ h2 {
2234
font-size: 3em;
2335
color: #333;
2436
margin-top: 0;
25-
letter-spacing: -0.05em;
37+
letter-spacing: -0.1em;
2638
}
2739

2840
#info h2 {

‎e2e/graphs.spec.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {test, expect} from '@playwright/test'
2+
3+
test('03_graphs.html — easing curve thumbnails', async ({page}) => {
4+
await page.goto('/examples/03_graphs.html', {waitUntil: 'load'})
5+
6+
// Wait for the 2000ms tween animations to finish drawing all curves.
7+
await page.waitForTimeout(2500)
8+
9+
// The grid of canvas thumbnails should now show complete easing curves.
10+
await expect(page).toHaveScreenshot('03_graphs.webp', {
11+
fullPage: true,
12+
maxDiffPixelRatio: 0.01,
13+
})
14+
})
78.5 KB
Loading

‎e2e/playwright.config.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {defineConfig} from '@playwright/test'
2+
3+
export default defineConfig({
4+
testDir: '.',
5+
snapshotPathTemplate: '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}',
6+
webServer: {
7+
command: 'python3 -m http.server 3333',
8+
cwd: process.cwd(),
9+
port: 3333,
10+
reuseExistingServer: true,
11+
},
12+
use: {
13+
baseURL: 'http://localhost:3333',
14+
viewport: {width: 1280, height: 1030},
15+
},
16+
})

‎examples/20_timeline.html‎

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ <h2>20 _ timeline</h2>
1919
<p>Sequencing with Timeline: sequential by default, parallel via offsets and labels.</p>
2020
</div>
2121
<div style="position: absolute; top: 100px; left: 400px">
22-
<div id="target1" data-x="0" data-rotation="0" class="box">sequential</div>
23-
<div id="target2" data-x="0" data-rotation="0" class="box">parallel</div>
24-
<div id="target3" data-x="0" data-rotation="0" class="box">labels + nesting</div>
22+
<div id="target1" data-y="0" data-rotation="0" class="box">sequential</div>
23+
<div id="target2" data-y="0" data-rotation="0" class="box">parallel</div>
24+
<div id="target3" data-y="0" data-rotation="0" class="box">labels + nesting</div>
2525
</div>
2626

2727
<script type="module">
@@ -35,43 +35,38 @@ <h2>20 _ timeline</h2>
3535
function init() {
3636
// 1. Sequential: add() appends after the last child (replaces chain).
3737
const target1 = document.getElementById('target1')
38-
const seq = new Timeline()
39-
.add(new Tween(target1.dataset).to({x: 200}, 800).easing(Easing.Quadratic.InOut))
40-
.add(new Tween(target1.dataset).to({x: 0}, 800).easing(Easing.Quadratic.InOut))
41-
.onUpdate(function () {
42-
updateBox(target1, target1.dataset)
43-
})
44-
seq.start()
45-
timelines.push(seq)
38+
const sequence = new Timeline()
39+
.add(new Tween(target1.dataset).to({y: 200}, 800).easing(Easing.Quadratic.InOut))
40+
.add(new Tween(target1.dataset).to({y: 0}, 800).easing(Easing.Quadratic.InOut))
41+
.onUpdate(() => updateBox(target1, target1.dataset))
42+
sequence.start()
43+
timelines.push(sequence)
4644

4745
// 2. Parallel: explicit offset 0 starts children together
4846
// (two tweens, two different properties of the same box).
4947
const target2 = document.getElementById('target2')
50-
const par = new Timeline()
51-
.add(new Tween(target2.dataset).to({x: 200}, 1000).easing(Easing.Sinusoidal.InOut), 0)
48+
const parallel = new Timeline()
49+
.add(new Tween(target2.dataset).to({y: 200}, 1000).easing(Easing.Sinusoidal.InOut), 0)
5250
.add(new Tween(target2.dataset).to({rotation: 360}, 1000).easing(Easing.Sinusoidal.InOut), 0)
53-
.onUpdate(function () {
54-
updateBox(target2, target2.dataset)
55-
})
56-
par.start()
57-
timelines.push(par)
51+
.add(new Tween(target2.dataset).to({y: 0}, 1000).easing(Easing.Sinusoidal.InOut))
52+
.onUpdate(() => updateBox(target2, target2.dataset))
53+
parallel.start()
54+
timelines.push(parallel)
5855

5956
// 3. Labels and nesting: an inner timeline inside an outer one.
6057
// The spin starts 200ms after the 'moved' label, leaving a gap.
6158
const target3 = document.getElementById('target3')
6259
const inner = new Timeline()
63-
.add(new Tween(target3.dataset).to({x: 200}, 600).easing(Easing.Quadratic.Out), 0)
60+
.add(new Tween(target3.dataset).to({y: 200}, 600).easing(Easing.Quadratic.Out), 0)
6461
.addLabel('moved', 600)
6562
.add(new Tween(target3.dataset).to({rotation: 180}, 600).easing(Easing.Quadratic.InOut), {
6663
at: 'moved',
6764
offset: 200,
6865
})
6966
.add(new Tween(target3.dataset).to({rotation: 0}, 600).easing(Easing.Quadratic.InOut))
70-
.add(new Tween(target3.dataset).to({x: 0}, 600).easing(Easing.Quadratic.InOut))
67+
.add(new Tween(target3.dataset).to({y: 0}, 600).easing(Easing.Quadratic.InOut))
7168

72-
const outer = new Timeline().add(inner, 0).onUpdate(function () {
73-
updateBox(target3, target3.dataset)
74-
})
69+
const outer = new Timeline().add(inner, 0).onUpdate(() => updateBox(target3, target3.dataset))
7570
outer.start()
7671
timelines.push(outer)
7772
}
@@ -86,7 +81,7 @@ <h2>20 _ timeline</h2>
8681

8782
function updateBox(box, params) {
8883
const s = box.style,
89-
transform = 'translateX(' + Math.floor(params.x) + 'px) rotate(' + Math.floor(params.rotation) + 'deg)'
84+
transform = 'translateY(' + Math.floor(params.y) + 'px) rotate(' + Math.floor(params.rotation) + 'deg)'
9085
s.webkitTransform = transform
9186
s.mozTransform = transform
9287
s.transform = transform

‎examples/css/style.css‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1+
@font-face {
2+
font-family: 'Allerta';
3+
font-style: normal;
4+
/* font-weight: 400; */
5+
font-display: swap;
6+
src: url("../font/Allerta/Allerta-Regular.woff2") format('woff2');
7+
8+
/* No idea what this is, copied from Google Fonts embed code */
9+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
10+
}
11+
112
body {
213
background: #fff;
3-
font-family: Helvetica, Arial, sans;
14+
font-family: Allerta, Helvetica, Arial, sans-serif;
15+
letter-spacing: -0.03em;
416
}
517

618
a {
@@ -39,7 +51,7 @@ button:hover {
3951
font-size: 3em;
4052
color: #333;
4153
margin-top: 0;
42-
letter-spacing: -0.05em;
54+
letter-spacing: -0.1em;
4355
}
4456

4557
#info h2 {
7.2 KB
Binary file not shown.

‎examples/font/Allerta/OFL.txt‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
Copyright (c) 2010, Matt McInerney (matt@pixelspread.com),
2+
with Reserved Font Name Allerta.
3+
4+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
5+
This license is copied below, and is also available with a FAQ at:
6+
https://openfontlicense.org
7+
8+
9+
-----------------------------------------------------------
10+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
11+
-----------------------------------------------------------
12+
13+
PREAMBLE
14+
The goals of the Open Font License (OFL) are to stimulate worldwide
15+
development of collaborative font projects, to support the font creation
16+
efforts of academic and linguistic communities, and to provide a free and
17+
open framework in which fonts may be shared and improved in partnership
18+
with others.
19+
20+
The OFL allows the licensed fonts to be used, studied, modified and
21+
redistributed freely as long as they are not sold by themselves. The
22+
fonts, including any derivative works, can be bundled, embedded,
23+
redistributed and/or sold with any software provided that any reserved
24+
names are not used by derivative works. The fonts and derivatives,
25+
however, cannot be released under any other type of license. The
26+
requirement for fonts to remain under this license does not apply
27+
to any document created using the fonts or their derivatives.
28+
29+
DEFINITIONS
30+
"Font Software" refers to the set of files released by the Copyright
31+
Holder(s) under this license and clearly marked as such. This may
32+
include source files, build scripts and documentation.
33+
34+
"Reserved Font Name" refers to any names specified as such after the
35+
copyright statement(s).
36+
37+
"Original Version" refers to the collection of Font Software components as
38+
distributed by the Copyright Holder(s).
39+
40+
"Modified Version" refers to any derivative made by adding to, deleting,
41+
or substituting -- in part or in whole -- any of the components of the
42+
Original Version, by changing formats or by porting the Font Software to a
43+
new environment.
44+
45+
"Author" refers to any designer, engineer, programmer, technical
46+
writer or other person who contributed to the Font Software.
47+
48+
PERMISSION & CONDITIONS
49+
Permission is hereby granted, free of charge, to any person obtaining
50+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
51+
redistribute, and sell modified and unmodified copies of the Font
52+
Software, subject to the following conditions:
53+
54+
1) Neither the Font Software nor any of its individual components,
55+
in Original or Modified Versions, may be sold by itself.
56+
57+
2) Original or Modified Versions of the Font Software may be bundled,
58+
redistributed and/or sold with any software, provided that each copy
59+
contains the above copyright notice and this license. These can be
60+
included either as stand-alone text files, human-readable headers or
61+
in the appropriate machine-readable metadata fields within text or
62+
binary files as long as those fields can be easily viewed by the user.
63+
64+
3) No Modified Version of the Font Software may use the Reserved Font
65+
Name(s) unless explicit written permission is granted by the corresponding
66+
Copyright Holder. This restriction only applies to the primary font name as
67+
presented to the users.
68+
69+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
70+
Software shall not be used to promote, endorse or advertise any
71+
Modified Version, except to acknowledge the contribution(s) of the
72+
Copyright Holder(s) and the Author(s) or with their explicit written
73+
permission.
74+
75+
5) The Font Software, modified or unmodified, in part or in whole,
76+
must be distributed entirely under this license, and must not be
77+
distributed under any other license. The requirement for fonts to
78+
remain under this license does not apply to any document created
79+
using the Font Software.
80+
81+
TERMINATION
82+
This license becomes null and void if any of the above conditions are
83+
not met.
84+
85+
DISCLAIMER
86+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
87+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
88+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
89+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
90+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
91+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
92+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
93+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
94+
OTHER DEALINGS IN THE FONT SOFTWARE.

0 commit comments

Comments
 (0)