Skip to content

Commit 99c9bee

Browse files
authored
feat(scaffold/test)!: watch mode for test, drop test.exitOnFinish option (#109)
* refactor: abstract watcher * stage: init test watch * refactor: mv constant * fix: better logger for test * fix: add space for build log * fix: use indents from http * feat: on-demand re-run * refactor: some renames and tweaks * fix: download zotero in ci * fix: import path error and remove baseUrl to force relative path import * fix: process exit on zotero exit * fix!: drop test.exitOnFinish option * tweaks * fix: serve duplicate logger
1 parent 02fae86 commit 99c9bee

25 files changed

Lines changed: 1045 additions & 750 deletions

docs/src/test.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,11 @@ export default defineConfig({
7272
mocha: {
7373
timeout: 10000
7474
},
75+
watch: true,
7576
abortOnFail: false,
76-
exitOnFinish: true,
7777
headless: false,
7878
startupDelay: 10000,
7979
waitForPlugin: `() => Zotero.MyPlugin.initialized`,
80-
watch: false,
8180
hooks: {}
8281
}
8382
});
@@ -99,8 +98,6 @@ To handle such cases, use the `test.waitForPlugin` configuration option. This op
9998

10099
## Watch Mode
101100

102-
This feature is still under development.
103-
104101
In watch mode, Scaffold automatically:
105102

106103
- Recompiles source code, reloads plugins, and reruns tests when the source changes.
@@ -119,6 +116,7 @@ Run tests
119116
Options:
120117
--abort-on-fail Abort the test suite on first failure
121118
--exit-on-finish Exit the test suite after all tests have run
119+
--no-watch Same with `exit-on-finish`
122120
-h, --help display help for command
123121
```
124122

packages/scaffold/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"fs-extra": "^11.3.0",
8181
"hookable": "^5.5.3",
8282
"octokit": "^4.1.2",
83+
"pathe": "^2.0.3",
8384
"std-env": "^3.8.1",
8485
"tiny-update-notifier": "^2.0.0",
8586
"tinyglobby": "^0.2.12",

packages/scaffold/src/cli.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,16 @@ async function main() {
5353
.description("Run tests")
5454
.option("--abort-on-fail", "Abort the test suite on first failure")
5555
.option("--exit-on-finish", "Exit the test suite after all tests have run")
56+
.option("--no-watch", "Exit the test suite after all tests have run")
5657
.action((options) => {
5758
process.env.NODE_ENV = "test";
5859

59-
Config.loadConfig({}).then((ctx) => {
60-
if (options.abortOnFail) {
61-
ctx.test.abortOnFail = true;
62-
}
63-
if (options.exitOnFinish) {
64-
ctx.test.exitOnFinish = true;
65-
}
60+
Config.loadConfig({
61+
test: {
62+
abortOnFail: options.abortOnFail,
63+
watch: !options.exitOnFinish && options.watch,
64+
},
65+
}).then((ctx) => {
6666
new Test(ctx).run();
6767
});
6868
});

packages/scaffold/src/config.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,10 @@ const defaultConfig = {
168168
timeout: 10000,
169169
},
170170
abortOnFail: false,
171-
exitOnFinish: false,
172171
headless: false,
173172
startupDelay: 1000,
174173
waitForPlugin: "() => true",
175-
watch: false,
174+
watch: true,
176175
hooks: {},
177176
},
178177
logLevel: "INFO",

packages/scaffold/src/constant.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const BASE_DIR = ".scaffold";
2+
export const CACHE_DIR = `${BASE_DIR}/cache`;
3+
export const DEFAULT_PROFILE_DIR = "";
4+
export const DEFAULT_DATA_DIR = "";
5+
6+
// Testser
7+
export const TESTER_BASE_PATH = `${BASE_DIR}/test`;
8+
export const TESTER_PROFILE_DIR = `${TESTER_BASE_PATH}/profile`;
9+
export const TESTER_DATA_DIR = `${TESTER_BASE_PATH}/data`;
10+
export const TESTER_PLUGIN_DIR = `${TESTER_BASE_PATH}/resource`;
11+
export const TESTER_PLUGIN_TESTS_DIR = `${TESTER_PLUGIN_DIR}/content/units`;
12+
export const TESTER_PLUGIN_REF = "zotero-plugin-scaffold-test-runner";
13+
export const TESTER_PLUGIN_ID = "[email protected]";

packages/scaffold/src/core/builder.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,28 +42,28 @@ export default class Build extends Base {
4242
await emptyDir(dist);
4343
await this.ctx.hooks.callHook("build:mkdir", this.ctx);
4444

45-
this.logger.tip("Preparing static assets");
45+
this.logger.tip("Preparing static assets", { space: 1 });
4646
await this.makeAssets();
4747
await this.ctx.hooks.callHook("build:copyAssets", this.ctx);
4848

49-
this.logger.debug("Preparing manifest");
49+
this.logger.debug("Preparing manifest", { space: 2 });
5050
await this.makeManifest();
5151
await this.ctx.hooks.callHook("build:makeManifest", this.ctx);
5252

53-
this.logger.debug("Preparing locale files");
53+
this.logger.debug("Preparing locale files", { space: 2 });
5454
await this.prepareLocaleFiles();
5555
await this.ctx.hooks.callHook("build:fluent", this.ctx);
5656

5757
await this.preparePrefs();
5858

59-
this.logger.tip("Bundling scripts");
59+
this.logger.tip("Bundling scripts", { space: 1 });
6060
await this.esbuild();
6161
await this.ctx.hooks.callHook("build:bundle", this.ctx);
6262

6363
/** ======== build resolved =========== */
6464

6565
if (process.env.NODE_ENV === "production") {
66-
this.logger.tip("Packing plugin");
66+
this.logger.tip("Packing plugin", { space: 1 });
6767
await this.pack();
6868
await this.ctx.hooks.callHook("build:pack", this.ctx);
6969

packages/scaffold/src/core/server.ts

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import type { Context } from "../types/index.js";
22
import { existsSync } from "node:fs";
33
import { join } from "node:path";
44
import process from "node:process";
5-
import chokidar from "chokidar";
6-
import { debounce } from "es-toolkit";
5+
import { watch } from "../utils/watcher.js";
76
import { ZoteroRunner } from "../utils/zotero-runner.js";
87
import { Base } from "./base.js";
98
import Build from "./builder.js";
@@ -69,47 +68,26 @@ export default class Serve extends Base {
6968
async watch() {
7069
const { source } = this.ctx;
7170

72-
const watcher = chokidar.watch(source, {
73-
ignored: /(^|[/\\])\../, // ignore dotfiles
74-
persistent: true,
75-
});
76-
77-
const onChangeDebounced = debounce(async (path: string) => {
78-
await this.onChange(path).catch((err) => {
79-
// Do not abort the watcher when errors occur
80-
// in builds triggered by the watcher.
81-
this.logger.error(err);
82-
});
83-
}, 500);
84-
85-
watcher
86-
.on("ready", async () => {
87-
await this.ctx.hooks.callHook("serve:ready", this.ctx);
88-
this.logger.clear();
89-
this.logger.ready("Server Ready!");
90-
})
91-
.on("change", async (path) => {
92-
this.logger.clear();
93-
this.logger.info(`${path} changed`);
94-
await onChangeDebounced(path);
95-
})
96-
.on("error", (err) => {
97-
this.logger.fail("Server start failed!");
98-
this.logger.error(err);
99-
});
100-
}
101-
102-
async onChange(path: string) {
103-
await this.ctx.hooks.callHook("serve:onChanged", this.ctx, path);
104-
105-
if (path.endsWith(".ts") || path.endsWith(".tsx")) {
106-
await this.builder.esbuild();
107-
}
108-
else {
109-
await this.builder.run();
110-
}
111-
112-
await this.reload();
71+
watch(
72+
source,
73+
{
74+
onReady: async () => {
75+
await this.ctx.hooks.callHook("serve:ready", this.ctx);
76+
},
77+
onChange: async (path) => {
78+
await this.ctx.hooks.callHook("serve:onChanged", this.ctx, path);
79+
80+
if (path.endsWith(".ts") || path.endsWith(".tsx")) {
81+
await this.builder.esbuild();
82+
}
83+
else {
84+
await this.builder.run();
85+
}
86+
87+
await this.reload();
88+
},
89+
},
90+
);
11391
}
11492

11593
async reload() {

0 commit comments

Comments
 (0)