rsnext/test/acceptance/helpers.js
Joe Haddad 61e7dea918
deps: upgrade various deps (mainly babel) (#20586)
Fixes #20585
Closes #20406 as it duplicates Babel dependencies
Closes #18926 as it's outdated
2020-12-29 20:10:08 +00:00

202 lines
6.4 KiB
JavaScript

import * as fs from 'fs-extra'
import { nanoid } from 'nanoid'
import { findPort, killApp, launchApp } from 'next-test-utils'
import webdriver from 'next-webdriver'
import path from 'path'
const rootSandboxDirectory = path.join(__dirname, '__tmp__')
export async function sandbox(
id = nanoid(),
initialFiles = new Map(),
defaultFiles = true
) {
const sandboxDirectory = path.join(rootSandboxDirectory, id)
const pagesDirectory = path.join(sandboxDirectory, 'pages')
await fs.remove(sandboxDirectory)
await fs.mkdirp(pagesDirectory)
if (defaultFiles) {
await fs.writeFile(
path.join(pagesDirectory, 'index.js'),
`export { default } from '../index';`
)
await fs.writeFile(
path.join(sandboxDirectory, 'index.js'),
`export default () => 'new sandbox';`
)
}
for (const [k, v] of initialFiles.entries()) {
await fs.mkdirp(path.dirname(path.join(sandboxDirectory, k)))
await fs.writeFile(path.join(sandboxDirectory, k), v)
}
const appPort = await findPort()
const app = await launchApp(sandboxDirectory, appPort, {
env: { __NEXT_TEST_WITH_DEVTOOL: 1 },
})
const browser = await webdriver(appPort, '/')
return [
{
sandboxDirectory,
async write(fileName, content) {
// Update the file on filesystem
const fullFileName = path.join(sandboxDirectory, fileName)
const dir = path.dirname(fullFileName)
await fs.mkdirp(dir)
await fs.writeFile(fullFileName, content)
},
async patch(fileName, content) {
// Register an event for HMR completion
await browser.executeScript(function () {
window.__HMR_STATE = 'pending'
var timeout = setTimeout(() => {
window.__HMR_STATE = 'timeout'
}, 30 * 1000)
window.__NEXT_HMR_CB = function () {
clearTimeout(timeout)
window.__HMR_STATE = 'success'
}
})
await this.write(fileName, content)
for (;;) {
const status = await browser.executeScript(() => window.__HMR_STATE)
if (!status) {
await new Promise((resolve) => setTimeout(resolve, 750))
// Wait for application to re-hydrate:
await browser.executeAsyncScript(function () {
var callback = arguments[arguments.length - 1]
if (window.__NEXT_HYDRATED) {
callback()
} else {
var timeout = setTimeout(callback, 30 * 1000)
window.__NEXT_HYDRATED_CB = function () {
clearTimeout(timeout)
callback()
}
}
})
console.log('Application re-loaded.')
// Slow down tests a bit:
await new Promise((resolve) => setTimeout(resolve, 750))
return false
}
if (status === 'success') {
console.log('Hot update complete.')
break
}
if (status !== 'pending') {
throw new Error(`Application is in inconsistent state: ${status}.`)
}
await new Promise((resolve) => setTimeout(resolve, 30))
}
// Slow down tests a bit (we don't know how long re-rendering takes):
await new Promise((resolve) => setTimeout(resolve, 750))
return true
},
async remove(fileName) {
const fullFileName = path.join(sandboxDirectory, fileName)
await fs.remove(fullFileName)
},
async evaluate() {
const input = arguments[0]
if (typeof input === 'function') {
const result = await browser.executeScript(input)
await new Promise((resolve) => setTimeout(resolve, 30))
return result
} else {
throw new Error(
`You must pass a function to be evaluated in the browser.`
)
}
},
async hasRedbox(expected = false) {
let attempts = 3
do {
const has = await this.evaluate(() => {
return Boolean(
[].slice
.call(document.querySelectorAll('nextjs-portal'))
.find((p) =>
p.shadowRoot.querySelector(
'#nextjs__container_errors_label, #nextjs__container_build_error_label'
)
)
)
})
if (has) {
return true
}
if (--attempts < 0) {
break
}
await new Promise((resolve) => setTimeout(resolve, 1000))
} while (expected)
return false
},
async getRedboxDescription() {
return await this.evaluate(() => {
const portal = [].slice
.call(document.querySelectorAll('nextjs-portal'))
.find((p) =>
p.shadowRoot.querySelector('[data-nextjs-dialog-header]')
)
const root = portal.shadowRoot
return root
.querySelector('#nextjs__container_errors_desc')
.innerText.replace(/__WEBPACK_DEFAULT_EXPORT__/, 'Unknown')
})
},
async getRedboxSource(includeHeader = false) {
const header = includeHeader
? await this.evaluate(() => {
const portal = [].slice
.call(document.querySelectorAll('nextjs-portal'))
.find((p) =>
p.shadowRoot.querySelector('[data-nextjs-dialog-header]')
)
const root = portal.shadowRoot
return root
.querySelector('[data-nextjs-dialog-header]')
.innerText.replace(/__WEBPACK_DEFAULT_EXPORT__/, 'Unknown')
})
: ''
const source = await this.evaluate(() => {
const portal = [].slice
.call(document.querySelectorAll('nextjs-portal'))
.find((p) =>
p.shadowRoot.querySelector(
'#nextjs__container_errors_label, #nextjs__container_build_error_label'
)
)
const root = portal.shadowRoot
return root.querySelector(
'[data-nextjs-codeframe], [data-nextjs-terminal]'
).innerText
})
if (includeHeader) {
return `${header}\n\n${source}`
}
return source
},
},
function cleanup() {
async function _cleanup() {
await browser.close()
await killApp(app)
}
_cleanup().catch(() => {})
},
]
}