Skip to content

Commit

Permalink
Fix test infra and navs
Browse files Browse the repository at this point in the history
  • Loading branch information
noamr committed Jan 5, 2024
1 parent cc996ff commit ff041d6
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 51 deletions.
31 changes: 18 additions & 13 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,21 @@ export function startViewTransition(options) {
return null;
}

const viewTransition = document.startViewTransition(options.update);

let viewTransition = /** @type {ViewTransition | null} */(null);

const afterUpdateCallback = new Promise(resolve => {
const update = async() => {
await options.update?.();
resolve(null);
}

viewTransition = document.startViewTransition(update);
});

start({
viewTransition,
afterUpdateCallback,
transitionFinished: viewTransition?.finished ?? Promise.resolve(),
captures: options.captures || {},
styles: options.styles || {},
classes: {both: options.classes || []}
Expand Down Expand Up @@ -150,20 +161,14 @@ export class Velvette {
handler: async () => {
/** @type {ViewTransition} */
let transition;
const updateCallbackDone = new Promise(resolve => {
const afterUpdateCallback = new Promise(resolve => {
transition = document.startViewTransition(async () => {
await interceptor.handler();
resolve(null);
});
transition.finished.then(() => done(null));
done(transition);
});

this.#internal.startNavigation(nav, {
get finished() { return transition.finished },
get ready() { return transition.ready },
updateCallbackDone,
skipTransition() { transition.skipTransition(); }
}, "both");
this.#internal.startNavigation(nav, afterUpdateCallback, transition.finished, "both");
}
}));
}
Expand Down Expand Up @@ -211,7 +216,7 @@ export class Velvette {
return;
}

this.#internal.startNavigation(nav, viewTransition, "new-only");
this.#internal.startNavigation(nav, Promise.resolve(), viewTransition.finished, "new-only");
result.dispatchEvent(new VelvetteEvent("inbound", viewTransition))
});

Expand All @@ -230,7 +235,7 @@ export class Velvette {
skipTransition: () => toggle(false)
};

this.#internal.startNavigation(nav, viewTransition, "old-only");
this.#internal.startNavigation(nav, Promise.resolve(), pageHidden, "old-only");
result.dispatchEvent(new VelvetteEvent("outbound", viewTransition));
});
return result;
Expand Down
22 changes: 12 additions & 10 deletions src/nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,17 @@ export function init(config) {
const params = {};
/**
*
* @param {URLPatternResult} result
* @returns {{[key: string]: string}}
* @param {URLPatternResult | null} result
* @returns {{[key: string]: string} | null}
*/
const applyParams = result => result ?
Object.assign(params, result?.pathname?.groups, result?.search?.groups) : {};
Object.assign(params, result?.pathname?.groups, result?.search?.groups) : null;

const defaultRoute = new URLPattern("*", location.href);
const from = routes.get(rule.from ?? "") ?? defaultRoute;
const to = routes.get(rule.to ?? "") ?? defaultRoute;
if (applyParams(from.exec(navigation.from)) &&
applyParams(to.exec(navigation.to)))
if (applyParams(from.test(navigation.from) ? from.exec(navigation.from) : null) &&
applyParams(to.test(navigation.to) ? to.exec(navigation.to) : null))
return {params, class: rule.class, from: rule.from, to: rule.to};
}

Expand All @@ -94,10 +94,11 @@ export function init(config) {
/**
*
* @param {NavigationInfo} nav
* @param {ViewTransition} viewTransition
* @param {PromiseLike<void>} afterUpdateCallback
* @param {PromiseLike<void>} transitionFinished
* @param {"old-only" | "new-only" | "both"} phase
*/
startNavigation(nav, viewTransition, phase) {
startNavigation(nav, afterUpdateCallback, transitionFinished, phase) {
/**
*
* @param {string} str
Expand All @@ -111,10 +112,11 @@ export function init(config) {
[sub(selector), sub(name)]));

return start({
viewTransition,
afterUpdateCallback,
transitionFinished,
classes: {
old: [`route-${nav.from}`],
new: [`route-${nav.to}`],
old: [`route-${nav.from}`, `with-${nav.to}`],
new: [`route-${nav.to}`, `with-${nav.from}`],
both: [`from-${nav.from}`, `to-${nav.to}`, ...(nav.class ? [nav.class] : [])]
},
styles: config.styles,
Expand Down
7 changes: 3 additions & 4 deletions src/transition.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ export async function start(transitionParams, phase) {

const oldClasses = [...(transitionParams.classes?.old || []), "old"];
const newClasses = [...(transitionParams.classes?.new || []), "new"];

if (phase !== "new-only") {
applyClasses(oldClasses, true);
applyClasses(transitionParams.classes?.both, true);
applyClasses(oldClasses, true);
applyCaptures();
await transitionParams.viewTransition.updateCallbackDone;
await transitionParams.afterUpdateCallback;
applyClasses(oldClasses, false);
cleanupCaptures();
}
Expand All @@ -29,7 +28,7 @@ export async function start(transitionParams, phase) {
applyClasses(newClasses, true);
applyCaptures();
document.adoptedStyleSheets.push(stylesheet);
await transitionParams.viewTransition.finished;
await transitionParams.transitionFinished;
document.adoptedStyleSheets.splice( document.adoptedStyleSheets.indexOf(stylesheet), 1);
cleanupCaptures();
applyClasses(newClasses, false);
Expand Down
3 changes: 2 additions & 1 deletion src/types-internal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference path="global.d.ts" />
export interface ViewTransitionParams {
viewTransition : ViewTransition
afterUpdateCallback: PromiseLike<void>
transitionFinished: PromiseLike<void>
captures?: {[selector: string]: string}
styles?: {[selector: string]: Partial<CSSStyleDeclaration>}
classes?: Partial<{
Expand Down
48 changes: 31 additions & 17 deletions tests/resources/list-details.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@
<main>
<section id="list">
<ul>
<li id="thing-1"><a href="?thing_id=1"><div class="thumb">1</div></a></li>
<li id="thing-2"><a href="?thing_id=2"><div class="thumb">2</div></a></li>
<li id="thing-3"><a href="?thing_id=3"><div class="thumb">3</div></a></li>
<li id="thing-1"><a href="?page=details&thing_id=1"><div class="thumb">1</div></a></li>
<li id="thing-2"><a href="?page=details&thing_id=2"><div class="thumb">2</div></a></li>
<li id="thing-3"><a href="?page=details&thing_id=3"><div class="thumb">3</div></a></li>
</ul>
</section>
<section id="details">
<div id="hero"></div>
<a href="?home">Home</a>
<a href="?page=home">Home</a>
</section>
</main>
<script type="module">
Expand All @@ -91,7 +91,7 @@
const velvette = new Velvette({
routes: {
list: "?page=home",
details: "?thing_id=:thingy"
details: "?page=details&thing_id=:thingy"
},
rules: [
{with: ["list", "details"]}
Expand All @@ -114,34 +114,48 @@
}
render();
let currentExpectation = null;
navigation.addEventListener("navigate", async e => {
const transition = await velvette.intercept(e, {
handler() {
render();
}
});
testViewTransition(transition, ["expander"], result =>
let currentTransition = null;
let next = null;
if (new URLSearchParams(location.search).has("page")) {
navigation.addEventListener("navigate", async event => {
const transition = await velvette.intercept(event, {
async handler() {
document.documentElement.dataset.page = new URLSearchParams(location.search).get("page") ?? "home";
}
});
currentTransition = transition;
await testViewTransition(transition, ["expander"], result =>
result ? result.filter(g => g.text === "group").map(l => l.y) : "no-transition",
currentExpectation, false);
});
if (next)
next();
});

} else {
location.href = "?page=home&start";
}


const tests = [];
function test(op, expect) {
tests.push({op, expect});
}

test(() => window.navigation.navigate("?thing_id=1"), [184.5, 179, 173.5]);
test(() => window.navigation.navigate("?page=details&thing_id=1"), [184.5,179,173.5]);
test(() => window.navigation.navigate("?page=home"), [289, 294.5, 300]);
test(() => window.navigation.navigate("?thing_id=2"), [388.5, 355, 321.5]);
test(() => window.navigation.navigate("?page=details&thing_id=2"), [388.5,355,321.5]);
// test(() => window.history.back(), [437, 470.5, 504]);
// test(() => window.navigation.navigate("?thing_id=3"), [592.5,531,469.5]);
// test(() => window.navigation.navigate("?thing_id=2"), "no-transition");
(async () => {
for (const test of tests) {
test.op();
const done = new Promise(resolve => {next = resolve});
currentExpectation = test.expect;
await new Promise(resolve => window.navigation.addEventListener("navigatesuccess", resolve));
test.op();
await done;
next = null;
currentTransition = null;
currentExpectation = null;
}

test_done();
Expand Down
20 changes: 15 additions & 5 deletions tests/resources/nav-spa.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,21 @@
]
});
let currentExpectation = null;
let currentTransition = null;
let next = null;
if (new URLSearchParams(location.search).has("page")) {
navigation.addEventListener("navigate", async event => {
const transition = await velvette.intercept(event, {
async handler() {
document.documentElement.dataset.page = new URLSearchParams(location.search).get("page") ?? "home";
}
});
testViewTransition(transition, ["main"], result =>
currentTransition = transition;
await testViewTransition(transition, ["main"], result =>
result ? result.filter(g => g.text === "group").map(l => l.x) : "no-transition",
currentExpectation, false);
if (next)
next();
});

} else {
Expand All @@ -47,19 +52,24 @@
tests.push({op, expect});
}

test(() => window.navigation.navigate("?page=details"), [100, 50, 0]);
test(() => window.navigation.navigate("?page=details"), [120, 60, 0]);
test(() => window.navigation.navigate("?page=home"), [100, 50, 0]);
test(() => window.navigation.navigate("?page=landing"), "no-transition");
// TODO: enable this once we can test on newer chrome.
// test(() => window.history.back(), [-100, -50, 0]);
// test(() => window.history.back(), [-100, -50, 0]);
(async () => {
for (const test of tests) {
test.op();
const done = new Promise(resolve => {next = resolve});
currentExpectation = test.expect;
await new Promise(resolve => window.navigation.addEventListener("navigatesuccess", resolve));
test.op();
await done;
next = null;
currentTransition = null;
currentExpectation
}

test_done();
window.navigation.navigate("?page=home");
})();
</script>
</body>
Expand Down
13 changes: 13 additions & 0 deletions tests/resources/nav.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ main {
}
}

@keyframes slide-left-far {
from {
transform: translateX(120px);
}
to {
transform: none;
}
}

@keyframes slide-right {
from {
transform: translateX(-100px);
Expand All @@ -30,4 +39,8 @@ main {
}
:root.vt-slide-right::view-transition-group(main) {
animation-name: slide-right;
}

:root.vt-route-details.vt-with-home::view-transition-group(main) {
animation-name: slide-left-far;
}
6 changes: 5 additions & 1 deletion tests/resources/pw-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export function testInBrowser(name, label, params = {}) {
const done = new Promise(resolve => {resolve_test = resolve});
await page.goto(url.href);
const result = await done;
if (result === "error")
continue;
if (result === "done") {
for (const {actual, expected} of expectations) {
expect(typeof actual).toEqual(typeof expected);
Expand All @@ -38,9 +40,11 @@ export function testInBrowser(name, label, params = {}) {
})
}
}
break;
return;
}
}

expect("this").toBe("not reached");
});
}

0 comments on commit ff041d6

Please sign in to comment.