Build a space page once when it opens
This commit is contained in:
parent
0ba6dc864c
commit
a7ca6bb9fb
3 changed files with 96 additions and 6 deletions
|
|
@ -1,8 +1,34 @@
|
|||
import {spec} from "@welshman/lib"
|
||||
import {HOUR, spec} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {RelayMessageType} from "@welshman/net"
|
||||
import {Article} from "@welshman/domain"
|
||||
import type {Page} from "@playwright/test"
|
||||
import {expect, getTranscript, pathPattern, roomPath, spacePath, test, users} from "../harness"
|
||||
|
||||
// Every page in a space renders one PageContent, so the number added to the document is the number
|
||||
// of times the page was built.
|
||||
const watchPageBuilds = (page: Page) =>
|
||||
page.evaluate(() => {
|
||||
const selector = "[data-component='PageContent']"
|
||||
|
||||
let builds = 0
|
||||
|
||||
document.documentElement.dataset.pageBuilds = "0"
|
||||
|
||||
new MutationObserver(records => {
|
||||
for (const record of records) {
|
||||
for (const node of record.addedNodes) {
|
||||
if (node instanceof Element && (node.matches(selector) || node.querySelector(selector))) {
|
||||
document.documentElement.dataset.pageBuilds = String(++builds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}).observe(document.body, {childList: true, subtree: true})
|
||||
})
|
||||
|
||||
const expectPageBuilds = (page: Page, builds: number) =>
|
||||
expect(page.locator("html")).toHaveAttribute("data-page-builds", String(builds))
|
||||
|
||||
test("keeps two spaces' contents on their own relays", async ({seed, as}) => {
|
||||
const scenario = await seed(({relay, user}) => {
|
||||
const space = relay("space")
|
||||
|
|
@ -202,3 +228,63 @@ test("enters a space on its details page whatever its relay advertises", async (
|
|||
await expect(page).toHaveURL(pathPattern(spacePath(space.url) + "/about"))
|
||||
await expect(page.locator('[data-component="PageBar"]')).toContainText("Space Details")
|
||||
})
|
||||
|
||||
test("builds a page once when it opens and again when its params change", async ({seed, as}) => {
|
||||
const scenario = await seed(({relay, user, at}) => {
|
||||
const space = relay("space")
|
||||
|
||||
space.room("general", {name: "General"})
|
||||
space.join(user.alice, "general")
|
||||
space.join(user.bob, "general")
|
||||
space.profile(user.alice, {name: "Alice Anderson"})
|
||||
space.relayList(user.alice)
|
||||
|
||||
const article = (identifier: string, title: string, hours: number) =>
|
||||
space.event(
|
||||
user.alice,
|
||||
() =>
|
||||
space
|
||||
.kind(Article)
|
||||
.writer()
|
||||
.setIdentifier(identifier)
|
||||
.setTitle(title)
|
||||
.setPublishedAt(at(hours, HOUR))
|
||||
.setContent("Gardens are worth the trouble.")
|
||||
.renderTemplate(),
|
||||
at(hours, HOUR),
|
||||
)
|
||||
|
||||
article("tending-the-garden", "Tending the Garden", 4)
|
||||
article("repotting-in-winter", "Repotting in Winter", 3)
|
||||
})
|
||||
|
||||
const {url} = scenario.space("space")
|
||||
const page = await as(users.bob, `${spacePath(url)}/articles`)
|
||||
|
||||
await watchPageBuilds(page)
|
||||
|
||||
await page
|
||||
.locator('[data-component="ArticleItem"]')
|
||||
.filter({hasText: "Tending the Garden"})
|
||||
.getByRole("link", {name: "Tending the Garden", exact: true})
|
||||
.click({position: {x: 20, y: 20}})
|
||||
|
||||
await expect(
|
||||
page.locator("article header").getByRole("heading", {name: "Tending the Garden"}),
|
||||
).toBeVisible()
|
||||
|
||||
// The rebuild this guards against landed 20ms after the first build, which is before the article
|
||||
// itself is on screen on a slower run.
|
||||
await page.waitForTimeout(250)
|
||||
await expectPageBuilds(page, 1)
|
||||
|
||||
// Another article is the same route with different params, which SvelteKit answers by keeping the
|
||||
// page it has. The page reads the address it renders once, so this one does have to be rebuilt.
|
||||
await page.getByRole("link", {name: "Repotting in Winter"}).click()
|
||||
|
||||
await expect(
|
||||
page.locator("article header").getByRole("heading", {name: "Repotting in Winter"}),
|
||||
).toBeVisible()
|
||||
|
||||
await expectPageBuilds(page, 2)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<script lang="ts">
|
||||
import type {Snippet} from "svelte"
|
||||
import {page} from "$app/stores"
|
||||
|
||||
type Props = {
|
||||
children?: Snippet
|
||||
params: {relay?: string}
|
||||
}
|
||||
|
||||
const {children}: Props = $props()
|
||||
const {children, params}: Props = $props()
|
||||
</script>
|
||||
|
||||
{#key $page.params.relay}
|
||||
{#key params.relay}
|
||||
{@render children?.()}
|
||||
{/key}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,10 @@
|
|||
|
||||
type Props = {
|
||||
children?: Snippet
|
||||
params: Record<string, string>
|
||||
}
|
||||
|
||||
const {children}: Props = $props()
|
||||
const {children, params}: Props = $props()
|
||||
|
||||
const url = decodeRelay($page.params.relay!)
|
||||
|
||||
|
|
@ -113,7 +114,10 @@
|
|||
<SpaceMenu {url} />
|
||||
</SecondaryNav>
|
||||
<Page>
|
||||
{#key $page.url.pathname}
|
||||
<!-- SvelteKit builds a new page when the route changes and keeps the one it has when only the
|
||||
params change, so this rebuilds it for the second case. The url the page store reports
|
||||
arrives a tick after the page is built, so keying on that throws the new page away. -->
|
||||
{#key JSON.stringify(params)}
|
||||
{@render children?.()}
|
||||
{/key}
|
||||
</Page>
|
||||
|
|
|
|||
Loading…
Reference in a new issue