Robin Malfait 7565099c1f
Integrations setup (#4354)
* add integration test tools

* setup jest in the integrations folder

* add `test:integrations` script

The default `npm test` script will ignore all the tests in the
`integrations` folder.

* add integration tests with `webpack 5`

* add integration tests with `postcss-cli`

* add `npm run install:integrations` script

This script will run `npm install` in every integration, and in the
integrations folder itself (to setup Jest for example).

* add `toIncludeCss` custom matcher

* increate Jest timeout for integration tests

* add integration tests with `vite`

* add integration tests with `webpack 4`

* add isomorphic fetch

* add the ability to wait for specific stdout/stderr output

* write vite tests, assert using API calls

We will wait for the correct stdout/stderr output, once we know that we
can request the fresh css, we will fetch it and make assertions
accordingly.

Port is currently hardcoded, maybe we should use a packaage to ensure
that we use a free port.

* add integration tests with `rollup`

* add integration tests with `parcel`

* run all integration tests in band

* add .gitignore in integrations folder
2021-05-18 11:21:35 -04:00

140 lines
4.0 KiB
JavaScript

let { rm, existsSync } = require('fs')
let path = require('path')
let fs = require('fs/promises')
let chokidar = require('chokidar')
let resolveToolRoot = require('./resolve-tool-root')
module.exports = function ({
/** Output directory, relative to the tool. */
output = 'dist',
/** Input directory, relative to the tool. */
input = 'src',
/** Whether or not you want to cleanup the output directory. */
cleanup = true,
} = {}) {
let toolRoot = resolveToolRoot()
let fileCache = {}
let absoluteOutputFolder = path.resolve(toolRoot, output)
let absoluteInputFolder = path.resolve(toolRoot, input)
if (cleanup) {
beforeAll((done) => rm(absoluteOutputFolder, { recursive: true, force: true }, done))
afterEach((done) => rm(absoluteOutputFolder, { recursive: true, force: true }, done))
}
// Restore all written files
afterEach(async () => {
await Promise.all(
Object.entries(fileCache).map(([file, content]) => fs.writeFile(file, content, 'utf8'))
)
})
async function readdir(start, parent = []) {
let files = await fs.readdir(start, { withFileTypes: true })
let resolvedFiles = await Promise.all(
files.map((file) => {
if (file.isDirectory()) {
return readdir(path.resolve(start, file.name), [...parent, file.name])
}
return parent.concat(file.name).join(path.sep)
})
)
return resolvedFiles.flat(Infinity)
}
async function resolveFile(fileOrRegex, directory) {
if (fileOrRegex instanceof RegExp) {
let files = await readdir(directory)
if (files.length === 0) {
throw new Error(`No files exists in "${directory}"`)
}
let filtered = files.filter((file) => fileOrRegex.test(file))
if (filtered.length === 0) {
throw new Error(`Not a single file matched: ${fileOrRegex}`)
} else if (filtered.length > 1) {
throw new Error(`Multiple files matched: ${fileOrRegex}`)
}
return filtered[0]
}
return fileOrRegex
}
return {
async readOutputFile(file) {
file = await resolveFile(file, absoluteOutputFolder)
return fs.readFile(path.resolve(absoluteOutputFolder, file), 'utf8')
},
async appendToInputFile(file, contents) {
let filePath = path.resolve(absoluteInputFolder, file)
if (!fileCache[filePath]) {
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
}
return fs.appendFile(filePath, contents, 'utf8')
},
async writeInputFile(file, contents) {
let filePath = path.resolve(absoluteInputFolder, file)
if (!fileCache[filePath]) {
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
}
return fs.writeFile(path.resolve(absoluteInputFolder, file), contents, 'utf8')
},
async waitForOutputFileCreation(file) {
if (file instanceof RegExp) {
let r = file
let watcher = chokidar.watch(absoluteOutputFolder)
return new Promise((resolve) => {
watcher.on('add', (file) => {
if (r.test(file)) {
watcher.close()
resolve()
}
})
})
} else {
let filePath = path.resolve(absoluteOutputFolder, file)
let watcher = chokidar.watch(filePath)
let watcherPromise = new Promise((resolve) => {
watcher.once('add', () => {
watcher.close()
resolve()
})
})
if (existsSync(filePath)) {
watcher.close()
return Promise.resolve()
}
return watcherPromise
}
},
async waitForOutputFileChange(file, cb = () => {}) {
file = await resolveFile(file, absoluteOutputFolder)
let filePath = path.resolve(absoluteOutputFolder, file)
let watcher = chokidar.watch(filePath)
return new Promise((resolve) => {
let chain = Promise.resolve()
watcher.once('change', () => {
watcher.close()
chain.then(() => resolve())
})
chain.then(() => cb())
})
},
}
}