mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
Fix for #348 - migrate our NextJS project to a pure webpack project w/ a single bundle - [x] Switch from `next/link` to `react-router-dom`'s link > This part was easy - just change the import to `import { Link } from "react-router-dom"` and `<Link href={...} />` to `<Link to={...} />` - [x] Switch from `next/router` to `react-router-dom`'s paradigms (`useNavigation`, `useLocation`, and `useParams`) > `router.push` can be converted to `navigate(...)` (provided by the `useNavigate` hook) > `router.replace` can be converted `navigate(..., {replace: true})` > Query parameters (`const { query } = useRouter`) can be converted to `const query = useParams()`) - [x] Implement client-side routing with `react-router-dom` > Parameterized routes in NextJS like `projects/[organization]/[project]` would look like: > ``` > <Route path="projects"> > <Route path=":organization/:project"> > <Route index element={<ProjectPage />} /> > </Route> > </Route> > ``` I've hooked up a `build:analyze` command that spins up a server to show the bundle size: <img width="1303" alt="image" src="https://user-images.githubusercontent.com/88213859/157496889-87c5fdcd-fad1-4f2e-b7b6-437aebf99641.png"> The bundle looks OK, but there are some opportunities for improvement - the heavy-weight dependencies, like React, ReactDOM, Material-UI, and lodash could be brought in via a CDN: https://stackoverflow.com/questions/50645796/how-to-import-reactjs-material-ui-using-a-cdn-through-webpacks-externals
83 lines
2.4 KiB
TypeScript
83 lines
2.4 KiB
TypeScript
import { Page } from "@playwright/test"
|
|
|
|
/**
|
|
* `timeout(x)` is a helper function to create a promise that resolves after `x` milliseconds.
|
|
*
|
|
* @param timeoutInMilliseconds Time to wait for promise to resolve
|
|
* @returns `Promise`
|
|
*/
|
|
export const timeout = (timeoutInMilliseconds: number): Promise<void> => {
|
|
return new Promise((resolve) => {
|
|
setTimeout(resolve, timeoutInMilliseconds)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* `waitFor(f, timeout?)` waits for a predicate to return `true`, running it periodically until it returns `true`.
|
|
*
|
|
* If `f` never returns `true`, the function will simply return. In other words, the burden is on the consumer
|
|
* to check that the predicate is passing (`waitFor` does no validation).
|
|
*
|
|
* @param f A predicate that returns a `Promise<boolean>`
|
|
* @param timeToWaitInMilliseconds The total time to wait for the condition to be `true`.
|
|
* @returns
|
|
*/
|
|
export const waitFor = async (f: () => Promise<boolean>, timeToWaitInMilliseconds = 30000): Promise<void> => {
|
|
let elapsedTime = 0
|
|
const timeToWaitPerIteration = 1000
|
|
|
|
while (elapsedTime < timeToWaitInMilliseconds) {
|
|
const condition = await f()
|
|
|
|
if (condition) {
|
|
return
|
|
}
|
|
|
|
await timeout(timeToWaitPerIteration)
|
|
elapsedTime += timeToWaitPerIteration
|
|
}
|
|
}
|
|
|
|
interface WaitForClientSideNavigationOpts {
|
|
/**
|
|
* from is the page before navigation (the 'current' page)
|
|
*/
|
|
from?: string
|
|
/**
|
|
* to is the page after navigation (the 'next' page)
|
|
*/
|
|
to?: string
|
|
}
|
|
|
|
/**
|
|
* waitForClientSideNavigation waits for the url to change from opts.from to
|
|
* opts.to (if specified), as well as a network idle load state. This enhances
|
|
* a native playwright check for navigation or loadstate.
|
|
*
|
|
* @remark This is necessary in a client-side SPA world since playwright
|
|
* waitForNavigation waits for load events on the DOM (ex: after a page load
|
|
* from the server).
|
|
*
|
|
* @todo Better logging for this.
|
|
*/
|
|
export const waitForClientSideNavigation = async (page: Page, opts: WaitForClientSideNavigationOpts): Promise<void> => {
|
|
await Promise.all([
|
|
waitFor(() => {
|
|
const conditions: boolean[] = []
|
|
|
|
if (opts.from) {
|
|
conditions.push(page.url() !== opts.from)
|
|
}
|
|
|
|
if (opts.to) {
|
|
conditions.push(page.url() === opts.to)
|
|
}
|
|
|
|
const unmetConditions = conditions.filter((condition) => !condition)
|
|
|
|
return Promise.resolve(unmetConditions.length === 0)
|
|
}),
|
|
page.waitForLoadState("networkidle"),
|
|
])
|
|
}
|