-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: add initial watch mode test suite
- test starting watch mode, changing a file, and adding a semantic error - put this in a separate file as it has its own complexity to deal with (see below) - initially put it inside `no-errors`, but it got pretty confusing; this structure is a good bit simpler - refactored this a couple times actually - add two watch mode helpers - `watchBundle` is very similar to `genBundle` but with some watch mode nuances (like returning a watcher) - `watchEnd` is a bit complicated async wrapper around a listener interface to make imperative testing simpler - refactor: abstract out `createInput` and `createOutput` to be used in both `genBundle` and `watchBundle` - refactor: make sure `dist` output gets put into a temp test dir - the previous config made it output into rpt2's `dist` dir, since `cwd` is project root (when running tests from project root) - the Rollup API's `watch` func always writes out; can't just test in-memory like with `bundle.generate` - so the `dist` dir becomes relevant as such - refactor: pass in a temp `testDir` instead of the `cacheRoot` - we can derive the `cacheRoot` and the `dist` output from `testDir` - also improve test clean-up by removing `testDir` at the end, not just the `cacheRoot` dir inside it - `testDir` is the same var used in the unit tests to define a temp dir for testing - change the `no-errors` fixture a tiny bit so that changing the import causes it to change too - this ended up being fairly complex due to having to handle lots of async and parallelism - parallel testing means fixtures have to be immutable, but watch mode needs to modify files - ended up copying fixtures to a temp dir where I could modify them - async events are all over here - had to convert a callback listener interface to async too, which was pretty confusing - and make sure the listener and bundles got properly closed too so no leaky tests - apparently `expect.rejects.toThrow` can return a Promise too, so missed this error for a while - refactor: make sure the error spec awaits too (though I think the errors _happen_ to throw synchronously there) - and also found a number of bugs while attempting to test cache invalidation within watch mode - thought it was a natural place to test since watch mode testing needs to modify files anyway - had to trace a bunch to figure out why some code paths weren't being covered; will discuss this separately from this commit - testing Rollup watch within Jest watch also causes Jest to re-run a few times - I imagine double, overlapping watchers are confusing each other - might need to disable these tests when using Jest watch - high complexity async + parallel flows makes it pretty confusing to debug, so this code needs to be "handled with care" - also this high complexity was even harder to debug when I'm having migraines (which is most of the time, unfortunately) - hence why it took me a bit to finally make a PR for this small amount of code; the code was ok, the debugging was not too fun
- Loading branch information
Showing
5 changed files
with
139 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { jest, beforeAll, afterAll, test, expect } from "@jest/globals"; | ||
import * as path from "path"; | ||
import * as fs from "fs-extra"; | ||
|
||
import { RPT2Options } from "../../src/index"; | ||
import * as helpers from "./helpers"; | ||
|
||
// increase timeout to 15s for whole file since CI occassionally timed out -- these are integration and cache tests, so longer timeout is warranted | ||
jest.setTimeout(15000); | ||
|
||
const local = (x: string) => path.resolve(__dirname, x); | ||
const testDir = local("__temp/watch"); | ||
const fixtureDir = `${testDir}/fixtures`; | ||
|
||
beforeAll(async () => { | ||
await fs.ensureDir(fixtureDir); | ||
// copy the dir to not interfere with other parallel tests since we need to change files for watch mode | ||
// note we're copying the root fixture dir bc we need the _base_ tsconfig too. maybe optimize in the future or use the other fixtures? | ||
await fs.copy(local("fixtures"), fixtureDir); | ||
}); | ||
afterAll(() => fs.remove(testDir)); | ||
|
||
async function watchBundle(input: string, extraOpts?: RPT2Options) { | ||
return helpers.watchBundle({ | ||
input, | ||
tsconfig: `${path.dirname(input)}/tsconfig.json`, // use the tsconfig of whatever fixture we're in | ||
testDir, | ||
extraOpts, | ||
}); | ||
} | ||
|
||
test("integration - watch", async () => { | ||
const srcPath = `${fixtureDir}/no-errors/index.ts`; | ||
const importPath = `${fixtureDir}/no-errors/some-import.ts`; | ||
const distDir = `${testDir}/dist`; | ||
const distPath = `${testDir}/dist/index.js`; | ||
const decPath = `${distDir}/index.d.ts`; | ||
const decMapPath = `${decPath}.map`; | ||
const filesArr = [ | ||
"index.js", | ||
"index.d.ts", | ||
"index.d.ts.map", | ||
"some-import.d.ts", | ||
"some-import.d.ts.map", | ||
"type-only-import.d.ts", | ||
"type-only-import.d.ts.map", | ||
]; | ||
|
||
const watcher = await watchBundle(srcPath); | ||
|
||
const files = await fs.readdir(distDir); | ||
expect(files).toEqual(expect.arrayContaining(filesArr)); | ||
expect(files.length).toBe(filesArr.length); // no other files | ||
|
||
// save content to test against later | ||
const dist = await fs.readFile(distPath, "utf8"); | ||
const dec = await fs.readFile(decPath, "utf8"); | ||
const decMap = await fs.readFile(decMapPath, "utf8"); | ||
|
||
// modify an imported file -- this should cause it and index to change | ||
await fs.writeFile(importPath, "export const difference = 2", "utf8"); | ||
await helpers.watchEnd(watcher); | ||
|
||
// should have same structure, since names haven't changed and dist hasn't been cleaned | ||
const files2 = await fs.readdir(distDir); | ||
expect(files2).toEqual(expect.arrayContaining(filesArr)); | ||
expect(files2.length).toBe(filesArr.length); // no other files | ||
|
||
// should have different content now though | ||
expect(dist).not.toEqual(await fs.readFile(distPath, "utf8")); | ||
expect(dec).not.toEqual(await fs.readFile(decPath, "utf8")); | ||
expect(decMap).not.toEqual(await fs.readFile(decMapPath, "utf8")); | ||
|
||
// modify an imported file to cause a semantic error | ||
await fs.writeFile(importPath, "export const difference = nonexistent", "utf8") | ||
await expect(helpers.watchEnd(watcher)).rejects.toThrow("Cannot find name 'nonexistent'."); | ||
|
||
await watcher.close(); | ||
}); |