Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for HMR #49206

Merged
merged 9 commits into from May 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/next-swc/crates/next-dev-tests/.gitignore
@@ -0,0 +1 @@
tests/temp
62 changes: 57 additions & 5 deletions packages/next-swc/crates/next-dev-tests/test-harness/harness.ts
Expand Up @@ -7,8 +7,10 @@ declare global {
// We need to extract only the call signature as `autoReady(jest.describe)` drops all the other properties
var describe: AutoReady<typeof jest.describe>
var it: AutoReady<typeof jest.it>
var READY: (arg: string) => void
var TURBOPACK_READY: (arg: string) => void
var TURBOPACK_CHANGE_FILE: (arg: string) => void
var nsObj: (obj: any) => any
var __turbopackFileChanged: (id: string, error: Error) => void

interface Window {
NEXT_HYDRATED?: boolean
Expand Down Expand Up @@ -62,8 +64,8 @@ function markReady() {
isReady = true
requestIdleCallback(
() => {
if (typeof READY === 'function') {
READY('')
if (typeof TURBOPACK_READY === 'function') {
TURBOPACK_READY('')
} else {
console.info(
'%cTurbopack tests:',
Expand All @@ -83,16 +85,28 @@ export function wait(ms: number): Promise<void> {
})
}

async function waitForPath(contentWindow: Window, path: string): Promise<void> {
export async function waitForCondition(
predicate: () => boolean,
timeout: number | null = null
): Promise<void> {
const start = Date.now()
while (true) {
if (contentWindow.location.pathname === path) {
if (predicate()) {
break
}

await wait(1)

if (timeout != null && Date.now() - start > timeout) {
throw new Error('Timed out waiting for condition')
}
}
}

async function waitForPath(contentWindow: Window, path: string): Promise<void> {
return waitForCondition(() => contentWindow.location.pathname === path)
}

/**
* Loads a new page in an iframe and waits for it to load.
*/
Expand Down Expand Up @@ -210,3 +224,41 @@ export function markAsHydrated() {
window.onNextHydrated()
}
}

const fileChangedResolvers: Map<
string,
{ resolve: (value: unknown) => void; reject: (error: Error) => void }
> = new Map()

globalThis.__turbopackFileChanged = (id: string, error?: Error) => {
const resolver = fileChangedResolvers.get(id)
if (resolver == null) {
throw new Error(`No resolver found for id ${id}`)
} else if (error != null) {
resolver.reject(error)
} else {
resolver.resolve(null)
}
}

function unsafeUniqueId(): string {
const LENGTH = 10
const BASE = 16
return Math.floor(Math.random() * Math.pow(BASE, LENGTH))
.toString(BASE)
.slice(0, LENGTH)
}

export async function changeFile(
path: string,
find: string,
replaceWith: string
) {
return new Promise((resolve, reject) => {
const id = unsafeUniqueId()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We could loop to make this safe:

Suggested change
const id = unsafeUniqueId()
let id;
while ((id = unsafeUniqueId())) {
if (!fileChangedResolvers.has(id)) break;
}


fileChangedResolvers.set(id, { resolve, reject })

TURBOPACK_CHANGE_FILE(JSON.stringify({ path, id, find, replaceWith }))
})
}