@tenonhq/dovetail-core 0.0.120 → 0.0.122

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ // `dove watch` is hidden from the `--help` command index but stays wired for
3
+ // humans (TenonHQ/Dovetail#155). These tests pin both halves: the help text
4
+ // never lists `watch` / `w` / `watchAllScopes`, and all three spellings still
5
+ // route to the watch handler. Mocks mirror commanderStrict.test.ts — the parser
6
+ // is under test, not the commands, which would hit ServiceNow.
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ jest.mock("../commands", () => ({
12
+ refreshCommand: jest.fn(),
13
+ pushCommand: jest.fn(),
14
+ downloadCommand: jest.fn(),
15
+ initCommand: jest.fn(),
16
+ buildCommand: jest.fn(),
17
+ deployCommand: jest.fn(),
18
+ statusCommand: jest.fn(),
19
+ taskClearCommand: jest.fn(),
20
+ }));
21
+ jest.mock("../allScopesCommands", () => ({
22
+ initScopesCommand: jest.fn(),
23
+ watchAllScopesCommand: jest.fn(),
24
+ }));
25
+ jest.mock("../updateSetCommands", () => ({
26
+ createUpdateSetCommand: jest.fn(),
27
+ switchUpdateSetCommand: jest.fn(),
28
+ listUpdateSetsCommand: jest.fn(),
29
+ showCurrentUpdateSetCommand: jest.fn(),
30
+ changeScopeCommand: jest.fn(),
31
+ showCurrentScopeCommand: jest.fn(),
32
+ }));
33
+ jest.mock("../dashboardCommand", () => ({ dashboardCommand: jest.fn() }));
34
+ jest.mock("../schemaCommand", () => ({
35
+ schemaPullCommand: jest.fn(),
36
+ schemaDiffCommand: jest.fn(),
37
+ schemaSnapshotsCommand: jest.fn(),
38
+ }));
39
+ jest.mock("../claudeCommand", () => ({ initClaudeCommand: jest.fn() }));
40
+ jest.mock("../createRecordCommand", () => ({ createRecordCommand: jest.fn() }));
41
+ jest.mock("../deleteRecordCommand", () => ({ deleteRecordCommand: jest.fn() }));
42
+ jest.mock("../reconcileCommand", () => ({ reconcileCommand: jest.fn() }));
43
+ jest.mock("../migrateCommand", () => ({ migrateCommand: jest.fn() }));
44
+ jest.mock("../loginCommand", () => ({ loginCommand: jest.fn() }));
45
+ jest.mock("../knowledgeDiffCommand", () => ({ knowledgeDiffCommand: jest.fn() }));
46
+ jest.mock("../clickupCommands", () => ({
47
+ clickupTasksCommand: jest.fn(),
48
+ clickupTaskCommand: jest.fn(),
49
+ clickupCreateCommand: jest.fn(),
50
+ clickupUpdateCommand: jest.fn(),
51
+ clickupCommentCommand: jest.fn(),
52
+ clickupSetupCommand: jest.fn(),
53
+ clickupTeamsCommand: jest.fn(),
54
+ clickupSpacesCommand: jest.fn(),
55
+ clickupListsCommand: jest.fn(),
56
+ }));
57
+ // --- Imports (after mocks) ---
58
+ const yargs_1 = __importDefault(require("yargs/yargs"));
59
+ const commander_1 = require("../commander");
60
+ const allScopesCommands_1 = require("../allScopesCommands");
61
+ /**
62
+ * @description Runs a fake `dove` invocation through a fresh, non-exiting parser.
63
+ * @param {string[]} args - Argv as the user would type it, minus the binary name.
64
+ * @returns {Promise<ParseResult>} The parse error (if any) and yargs' captured output.
65
+ */
66
+ async function run(args) {
67
+ const cli = (0, commander_1.configureCli)((0, yargs_1.default)([])).exitProcess(false);
68
+ const result = await new Promise(function (resolve) {
69
+ cli.parse(args, {}, function (error, _argv, output) {
70
+ resolve({
71
+ error: error === null ? undefined : error,
72
+ output: output || "",
73
+ });
74
+ });
75
+ });
76
+ await new Promise(function (resolve) {
77
+ setImmediate(resolve);
78
+ });
79
+ return result;
80
+ }
81
+ /**
82
+ * @description Extracts the `dove <cmd>` command names listed in a yargs help
83
+ * body (the "Commands:" index), so assertions target the index rather than
84
+ * incidental prose in option descriptions.
85
+ * @param {string} help - Captured `--help` output.
86
+ * @returns {string[]} Command tokens exactly as the index prints them.
87
+ */
88
+ function listedCommands(help) {
89
+ const names = [];
90
+ const lines = help.split("\n");
91
+ for (let i = 0; i < lines.length; i++) {
92
+ const match = /^\s*dove\s+(\S+)/.exec(lines[i]);
93
+ if (match)
94
+ names.push(match[1]);
95
+ }
96
+ return names;
97
+ }
98
+ beforeEach(function () {
99
+ jest.clearAllMocks();
100
+ });
101
+ describe("dove --help hides watch", function () {
102
+ it("does not list watch, w, or watchAllScopes in the command index", async function () {
103
+ const { output } = await run(["--help"]);
104
+ const commands = listedCommands(output);
105
+ expect(commands.length).toBeGreaterThan(0);
106
+ expect(commands).not.toContain("watch");
107
+ expect(commands).not.toContain("w");
108
+ expect(commands).not.toContain("watchAllScopes");
109
+ expect(output).not.toMatch(/\bwatchAllScopes\b/);
110
+ expect(output).not.toMatch(/\bdove\s+w(atch)?\b/);
111
+ });
112
+ it("still lists the safe headless verbs", async function () {
113
+ const { output } = await run(["--help"]);
114
+ const commands = listedCommands(output);
115
+ expect(commands).toContain("refresh");
116
+ expect(commands).toContain("push");
117
+ expect(commands).toContain("status");
118
+ });
119
+ it("does not surface watch in the bare-invocation help either", async function () {
120
+ const { output, error } = await run([]);
121
+ expect(error).toBeDefined();
122
+ expect(output + String(error)).not.toMatch(/\bwatchAllScopes\b/);
123
+ expect(listedCommands(output)).not.toContain("watch");
124
+ });
125
+ });
126
+ describe("dove watch stays wired for humans", function () {
127
+ it("routes `watch` to the watch handler", async function () {
128
+ const { error } = await run(["watch"]);
129
+ expect(error).toBeUndefined();
130
+ expect(allScopesCommands_1.watchAllScopesCommand).toHaveBeenCalledTimes(1);
131
+ });
132
+ it("routes the `w` and `watchAllScopes` aliases too", async function () {
133
+ await run(["w"]);
134
+ expect(allScopesCommands_1.watchAllScopesCommand).toHaveBeenCalledTimes(1);
135
+ await run(["watchAllScopes"]);
136
+ expect(allScopesCommands_1.watchAllScopesCommand).toHaveBeenCalledTimes(2);
137
+ });
138
+ it("passes watch flags through", async function () {
139
+ await run(["watch", "--noDashboard", "--port", "4000"]);
140
+ expect(allScopesCommands_1.watchAllScopesCommand).toHaveBeenCalledTimes(1);
141
+ const mock = allScopesCommands_1.watchAllScopesCommand;
142
+ const args = mock.mock.calls[0][0];
143
+ expect(args.noDashboard).toBe(true);
144
+ expect(args.port).toBe(4000);
145
+ });
146
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/dovetail-core",
3
- "version": "0.0.120",
3
+ "version": "0.0.122",
4
4
  "description": "Next-gen file syncer",
5
5
  "license": "GPL-3.0",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "node": ">=22"
23
23
  },
24
24
  "devDependencies": {
25
- "@tenonhq/dovetail-types": "~0.0.21",
25
+ "@tenonhq/dovetail-types": "~0.0.22",
26
26
  "@types/dotenv": "^6.1.1",
27
27
  "@types/inquirer": "^8.2.10",
28
28
  "@types/jest": "^29.5.5",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@tenonhq/dovetail-clickup": "~0.0.14",
42
- "@tenonhq/dovetail-schema": "~0.0.14",
42
+ "@tenonhq/dovetail-schema": "~0.0.15",
43
43
  "axios": "^1.20.0",
44
44
  "axios-cookiejar-support": "^4.0.7",
45
45
  "axios-rate-limit": "^1.3.0",