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 type {TrustedEvent} from "@welshman/util"
|
||||||
import {RelayMessageType} from "@welshman/net"
|
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"
|
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}) => {
|
test("keeps two spaces' contents on their own relays", async ({seed, as}) => {
|
||||||
const scenario = await seed(({relay, user}) => {
|
const scenario = await seed(({relay, user}) => {
|
||||||
const space = relay("space")
|
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).toHaveURL(pathPattern(spacePath(space.url) + "/about"))
|
||||||
await expect(page.locator('[data-component="PageBar"]')).toContainText("Space Details")
|
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">
|
<script lang="ts">
|
||||||
import type {Snippet} from "svelte"
|
import type {Snippet} from "svelte"
|
||||||
import {page} from "$app/stores"
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children?: Snippet
|
children?: Snippet
|
||||||
|
params: {relay?: string}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {children}: Props = $props()
|
const {children, params}: Props = $props()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key $page.params.relay}
|
{#key params.relay}
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
{/key}
|
{/key}
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,10 @@
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children?: Snippet
|
children?: Snippet
|
||||||
|
params: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
const {children}: Props = $props()
|
const {children, params}: Props = $props()
|
||||||
|
|
||||||
const url = decodeRelay($page.params.relay!)
|
const url = decodeRelay($page.params.relay!)
|
||||||
|
|
||||||
|
|
@ -113,7 +114,10 @@
|
||||||
<SpaceMenu {url} />
|
<SpaceMenu {url} />
|
||||||
</SecondaryNav>
|
</SecondaryNav>
|
||||||
<Page>
|
<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?.()}
|
{@render children?.()}
|
||||||
{/key}
|
{/key}
|
||||||
</Page>
|
</Page>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue