Files
coder/site/e2e/util.ts
T
Bryan ec077c6191 refactor: Migrate from Next.js to pure webpack config (#360)
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
2022-03-12 12:51:05 -08:00

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"),
])
}