@canonical/pragma-cli 0.30.0 → 0.31.0
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.
- package/README.md +64 -35
- package/dist/pragma +0 -0
- package/package.json +15 -13
- package/src/completions/constants.ts +7 -0
- package/src/completions/handleQuery.test.ts +32 -0
- package/src/completions/handleQuery.ts +20 -1
- package/src/completions/queryCompletions.test.ts +5 -0
- package/src/completions/queryCompletions.ts +17 -3
- package/src/completions/querySocket.test.ts +31 -0
- package/src/completions/querySocket.ts +36 -9
- package/src/config/parseConfigValues.ts +2 -0
- package/src/config/readConfigLayers.test.ts +2 -0
- package/src/config/readConfigLayers.ts +3 -0
- package/src/config/types.ts +7 -0
- package/src/constants.ts +3 -1
- package/src/domains/block/commands/sample.ts +2 -1
- package/src/domains/block/formatters/list.test.ts +3 -3
- package/src/domains/block/formatters/lookup.test.ts +5 -5
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
- package/src/domains/block/stories.ts +9 -3
- package/src/domains/config/commands/channel.test.ts +16 -0
- package/src/domains/config/commands/channel.ts +14 -1
- package/src/domains/config/commands/framework.test.ts +14 -0
- package/src/domains/config/commands/framework.ts +13 -1
- package/src/domains/config/commands/tier.test.ts +7 -0
- package/src/domains/config/commands/tier.ts +14 -1
- package/src/domains/config/operations/validateTier.test.ts +9 -0
- package/src/domains/config/operations/validateTier.ts +10 -2
- package/src/domains/create/generatorPack/bundled.ts +49 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
- package/src/domains/create/index.ts +13 -7
- package/src/domains/create/mcp/specs.ts +6 -161
- package/src/domains/create/renderGeneratorUi.test.ts +103 -0
- package/src/domains/create/renderGeneratorUi.ts +65 -0
- package/src/domains/doctor/formatters/doctor.test.ts +50 -2
- package/src/domains/doctor/formatters/doctor.ts +90 -38
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +9 -6
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +30 -12
- package/src/domains/doctor/operations/checks.test.ts +6 -4
- package/src/domains/doctor/operations/types.ts +23 -1
- package/src/domains/graph/commands/inspect.test.ts +2 -2
- package/src/domains/graph/commands/query.test.ts +5 -3
- package/src/domains/graph/formatters/inspect.ts +7 -7
- package/src/domains/graph/formatters/query.test.ts +87 -0
- package/src/domains/graph/formatters/query.ts +42 -5
- package/src/domains/graph/operations/inspectUri.test.ts +5 -5
- package/src/domains/graphql/commands/serve.test.ts +12 -6
- package/src/domains/graphql/commands/serve.ts +16 -6
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
- package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
- package/src/domains/graphql/operations/compileSchema.test.ts +2 -2
- package/src/domains/graphql/operations/createSchemaServer.test.ts +2 -2
- package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
- package/src/domains/graphql/operations/index.ts +5 -0
- package/src/domains/info/formatters/upgrade.test.ts +7 -4
- package/src/domains/info/formatters/upgrade.ts +3 -1
- package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
- package/src/domains/info/operations/collectEntityCounts.ts +123 -0
- package/src/domains/info/operations/index.ts +1 -0
- package/src/domains/info/types.ts +13 -0
- package/src/domains/llm/capabilities.test.ts +23 -3
- package/src/domains/llm/commands/capabilities.ts +12 -3
- package/src/domains/llm/commands/llm.ts +5 -1
- package/src/domains/llm/data/commandReference.ts +20 -11
- package/src/domains/llm/data/decisionTrees.ts +10 -5
- package/src/domains/llm/data/toolCatalog.ts +49 -18
- package/src/domains/llm/formatters/orientation.ts +5 -1
- package/src/domains/llm/llmCommand.test.ts +11 -3
- package/src/domains/llm/mcp/specs.ts +3 -4
- package/src/domains/llm/operations/collectContext.test.ts +64 -0
- package/src/domains/llm/operations/collectContext.ts +7 -17
- package/src/domains/modifier/commands/sample.ts +2 -1
- package/src/domains/modifier/operations/lookup.ts +5 -3
- package/src/domains/modifier/stories.ts +9 -0
- package/src/domains/ontology/commands/list.test.ts +5 -3
- package/src/domains/ontology/commands/show.test.ts +85 -9
- package/src/domains/ontology/formatters/index.ts +1 -0
- package/src/domains/ontology/formatters/list.ts +37 -12
- package/src/domains/ontology/formatters/show.ts +358 -67
- package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
- package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
- package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
- package/src/domains/ontology/helpers/index.ts +14 -0
- package/src/domains/ontology/helpers/queryClasses.ts +32 -16
- package/src/domains/ontology/helpers/queryProperties.ts +39 -6
- package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
- package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
- package/src/domains/ontology/operations/listOntologies.ts +126 -30
- package/src/domains/ontology/operations/showOntology.test.ts +131 -12
- package/src/domains/ontology/operations/showOntology.ts +289 -7
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
- package/src/domains/ontology/stories.ts +53 -8
- package/src/domains/refs/operations/updateRefs.ts +0 -1
- package/src/domains/setup/commands/index.ts +1 -0
- package/src/domains/setup/commands/setup.ts +70 -0
- package/src/domains/setup/index.ts +7 -3
- package/src/domains/setup/operations/setupLsp.test.ts +3 -2
- package/src/domains/setup/operations/setupLsp.ts +7 -2
- package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
- package/src/domains/shared/bootStore.test.ts +82 -0
- package/src/domains/shared/bootStore.ts +56 -20
- package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
- package/src/domains/shared/extractLocalName.test.ts +3 -3
- package/src/domains/shared/formatters.ts +23 -2
- package/src/domains/shared/listTiers.ts +40 -0
- package/src/domains/shared/loaders/bundledLoader.ts +48 -0
- package/src/domains/shared/loaders/gitLoader.ts +1 -2
- package/src/domains/shared/loaders/localLoader.ts +2 -18
- package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
- package/src/domains/shared/loaders/readPackageDir.ts +46 -8
- package/src/domains/shared/lookupMany.test.ts +15 -0
- package/src/domains/shared/lookupMany.ts +22 -6
- package/src/domains/shared/namespaces.ts +13 -5
- package/src/domains/shared/parseSampleCount.test.ts +47 -0
- package/src/domains/shared/parseSampleCount.ts +45 -0
- package/src/domains/shared/prefixes.test.ts +211 -0
- package/src/domains/shared/prefixes.ts +182 -15
- package/src/domains/shared/renderers.ts +5 -5
- package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
- package/src/domains/shared/resolveLookupExitCode.ts +42 -0
- package/src/domains/shared/semanticPackage.test.ts +89 -0
- package/src/domains/shared/semanticPackage.ts +17 -2
- package/src/domains/shared/stories/compileLookupCommand.ts +3 -1
- package/src/domains/shared/stories/compileLookupTool.ts +1 -1
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +49 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +46 -11
- package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
- package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +30 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +75 -1
- package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
- package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
- package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +305 -7
- package/src/domains/shared/stories/pack/collectPackStories.ts +90 -8
- package/src/domains/shared/stories/pack/compilePackCommands.ts +17 -7
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +607 -2
- package/src/domains/shared/stories/pack/compilePackStories.ts +544 -73
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +20 -8
- package/src/domains/shared/stories/pack/index.ts +14 -0
- package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
- package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +293 -220
- package/src/domains/shared/stories/pack/types.ts +123 -3
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +502 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +475 -24
- package/src/domains/shared/stories/types.ts +9 -1
- package/src/domains/shared/types/index.ts +5 -9
- package/src/domains/shared/types/ontology.ts +119 -24
- package/src/domains/shared/types/standard.ts +9 -52
- package/src/domains/token/commands/lookup.test.ts +8 -0
- package/src/domains/token/commands/lookup.ts +12 -0
- package/src/domains/token/commands/sample.ts +2 -1
- package/src/domains/token/featureFlag.ts +17 -0
- package/src/domains/token/index.ts +5 -0
- package/src/domains/token/mcp/specs.ts +13 -1
- package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
- package/src/domains/trace/commands/show.ts +30 -4
- package/src/index.ts +1 -13
- package/src/mcp/registerResources.test.ts +2 -2
- package/src/mcp/resources/pickPrimaryType.test.ts +5 -5
- package/src/mcp/tools/index.ts +18 -10
- package/src/mcp/tools/registerTools.test.ts +106 -30
- package/src/pipeline/collectCommands.test.ts +151 -1
- package/src/pipeline/collectCommands.ts +34 -19
- package/src/pipeline/createProgram.test.ts +5 -1
- package/src/pipeline/createProgram.ts +2 -2
- package/src/pipeline/firstRun.test.ts +80 -0
- package/src/pipeline/firstRun.ts +99 -0
- package/src/pipeline/parseGlobalFlags.test.ts +102 -1
- package/src/pipeline/parseGlobalFlags.ts +98 -9
- package/src/pipeline/resolveCommandKind.test.ts +12 -0
- package/src/pipeline/resolveCommandKind.ts +4 -0
- package/src/pipeline/rootHelp.test.ts +87 -0
- package/src/pipeline/rootHelp.ts +265 -0
- package/src/pipeline/runCli.orchestration.test.ts +28 -12
- package/src/pipeline/runCli.test.ts +17 -4
- package/src/pipeline/runCli.ts +162 -19
- package/src/testing/cli.ts +30 -2
- package/src/testing/dsFixtures.ts +21 -4
- package/src/testing/index.ts +1 -4
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +4 -4
- package/src/testing/integration/cli-user-stories.test.ts +63 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +2 -2
- package/src/testing/integration/edge-cases.test.ts +62 -50
- package/src/testing/integration/executor-execution.test.ts +6 -4
- package/src/testing/integration/executor-golden.test.ts +2 -2
- package/src/testing/integration/mcp-surface.test.ts +40 -15
- package/src/testing/integration/parity.test.ts +119 -70
- package/src/testing/setupXdgIsolation.ts +16 -5
- package/src/testing/standardParityFixtures.ts +26 -83
- package/src/testing/store.ts +4 -4
- package/src/domains/create/commands/component.test.ts +0 -166
- package/src/domains/create/commands/component.ts +0 -123
- package/src/domains/create/commands/index.ts +0 -3
- package/src/domains/create/commands/package.test.ts +0 -35
- package/src/domains/create/commands/package.ts +0 -21
- package/src/domains/create/generators.ts +0 -8
- package/src/domains/standard/commands/categories.test.ts +0 -70
- package/src/domains/standard/commands/categories.ts +0 -48
- package/src/domains/standard/commands/index.ts +0 -6
- package/src/domains/standard/commands/list.test.ts +0 -105
- package/src/domains/standard/commands/list.ts +0 -11
- package/src/domains/standard/commands/lookup.test.ts +0 -135
- package/src/domains/standard/commands/lookup.ts +0 -14
- package/src/domains/standard/commands/sample.ts +0 -57
- package/src/domains/standard/formatters/categories.test.ts +0 -44
- package/src/domains/standard/formatters/categories.ts +0 -38
- package/src/domains/standard/formatters/index.ts +0 -10
- package/src/domains/standard/formatters/list.test.ts +0 -169
- package/src/domains/standard/formatters/list.ts +0 -184
- package/src/domains/standard/formatters/lookup.test.ts +0 -95
- package/src/domains/standard/formatters/lookup.ts +0 -101
- package/src/domains/standard/formatters/sample.ts +0 -15
- package/src/domains/standard/formatters/types.ts +0 -31
- package/src/domains/standard/index.ts +0 -40
- package/src/domains/standard/mcp/index.ts +0 -2
- package/src/domains/standard/mcp/specs.ts +0 -89
- package/src/domains/standard/operations/categories.test.ts +0 -61
- package/src/domains/standard/operations/categories.ts +0 -38
- package/src/domains/standard/operations/index.ts +0 -6
- package/src/domains/standard/operations/list.test.ts +0 -75
- package/src/domains/standard/operations/list.ts +0 -64
- package/src/domains/standard/operations/lookup.test.ts +0 -58
- package/src/domains/standard/operations/lookup.ts +0 -151
- package/src/domains/standard/operations/sample.ts +0 -35
- package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
- package/src/domains/standard/orchestration/index.ts +0 -4
- package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
- package/src/domains/standard/standardConfig.ts +0 -42
- package/src/domains/standard/stories.test.ts +0 -24
- package/src/domains/standard/stories.ts +0 -151
- package/src/domains/standard/types.ts +0 -15
- package/src/domains/tier/commands/index.ts +0 -3
- package/src/domains/tier/commands/list.test.ts +0 -67
- package/src/domains/tier/commands/list.ts +0 -9
- package/src/domains/tier/formatters/index.ts +0 -3
- package/src/domains/tier/formatters/list.test.ts +0 -33
- package/src/domains/tier/formatters/list.ts +0 -45
- package/src/domains/tier/index.ts +0 -17
- package/src/domains/tier/mcp/index.ts +0 -2
- package/src/domains/tier/mcp/specs.ts +0 -12
- package/src/domains/tier/operations/index.ts +0 -3
- package/src/domains/tier/operations/list.test.ts +0 -32
- package/src/domains/tier/operations/list.ts +0 -33
- package/src/domains/tier/stories.test.ts +0 -22
- package/src/domains/tier/stories.ts +0 -52
|
@@ -42,20 +42,10 @@ import {
|
|
|
42
42
|
} from "../../domains/ontology/operations/index.js";
|
|
43
43
|
import { CHANNEL_RELEASES } from "../../domains/shared/filters/buildChannelFilter.js";
|
|
44
44
|
import { resolveTierChain } from "../../domains/shared/filters/buildTierFilter.js";
|
|
45
|
+
import { listTiers } from "../../domains/shared/listTiers.js";
|
|
45
46
|
import type { PragmaRuntime } from "../../domains/shared/runtime.js";
|
|
46
|
-
import {
|
|
47
|
-
|
|
48
|
-
categoriesFormatters as standardCatFmt,
|
|
49
|
-
listFormatters as standardListFmt,
|
|
50
|
-
lookupFormatters as standardLookupFmt,
|
|
51
|
-
} from "../../domains/standard/formatters/index.js";
|
|
52
|
-
import {
|
|
53
|
-
listCategories,
|
|
54
|
-
listStandards,
|
|
55
|
-
lookupStandard,
|
|
56
|
-
} from "../../domains/standard/operations/index.js";
|
|
57
|
-
import { listFormatters as tierListFmt } from "../../domains/tier/formatters/index.js";
|
|
58
|
-
import { listTiers } from "../../domains/tier/operations/index.js";
|
|
47
|
+
import { listDomainNames } from "../../domains/shared/suggestions/index.js";
|
|
48
|
+
import { TOKEN_READ_SURFACE_ENABLED } from "../../domains/token/featureFlag.js";
|
|
59
49
|
import {
|
|
60
50
|
createLookupFormatters as createTokenLookupFmt,
|
|
61
51
|
listFormatters as tokenListFmt,
|
|
@@ -176,58 +166,83 @@ describe("block parity", () => {
|
|
|
176
166
|
// Standard parity
|
|
177
167
|
// ---------------------------------------------------------------------------
|
|
178
168
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
169
|
+
// `standard *` is now served by the bundled `standard` story pack (the
|
|
170
|
+
// hand-written domain was deleted). Pack rows/entities are uniform
|
|
171
|
+
// `Record<string,string>` projections with expand arrays, and rendering is
|
|
172
|
+
// the generic pack renderer — so these tests assert *semantic* parity
|
|
173
|
+
// against the store (every standard reachable, by the same names, with the
|
|
174
|
+
// same category/description/dos/donts values) rather than byte equality
|
|
175
|
+
// with the removed bespoke formatters. See PARITY_GAPS in #testing for the
|
|
176
|
+
// pinned, accepted divergences.
|
|
177
|
+
describe("standard parity (bundled pack)", () => {
|
|
178
|
+
it("standard_list returns one {uri, name, category, description} row per standard", async () => {
|
|
179
|
+
const allNames = await listDomainNames(rt.store, "standard");
|
|
180
|
+
expect(allNames.length).toBeGreaterThan(0);
|
|
181
|
+
|
|
182
182
|
const mcpRes = await client.callTool({
|
|
183
183
|
name: "standard_list",
|
|
184
184
|
arguments: {},
|
|
185
185
|
});
|
|
186
|
-
|
|
186
|
+
const body = parseEnvelope(mcpRes);
|
|
187
|
+
expect(body.ok).toBe(true);
|
|
188
|
+
const rows = body.data as {
|
|
189
|
+
uri?: string;
|
|
190
|
+
name?: string;
|
|
191
|
+
category?: string;
|
|
192
|
+
description?: string;
|
|
193
|
+
}[];
|
|
194
|
+
|
|
195
|
+
// Same standards, same names — nothing dropped or invented.
|
|
196
|
+
const listed = new Set(rows.map((row) => row.name));
|
|
197
|
+
for (const name of allNames) {
|
|
198
|
+
expect(listed.has(name), `"${name}" missing from standard_list`).toBe(
|
|
199
|
+
true,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
for (const row of rows) {
|
|
203
|
+
expect(row.uri).toBeTruthy();
|
|
204
|
+
expect(row.description).toBeTruthy();
|
|
205
|
+
}
|
|
187
206
|
});
|
|
188
207
|
|
|
189
|
-
it("standard_list condensed
|
|
190
|
-
const
|
|
191
|
-
const output: StandardListOutput = {
|
|
192
|
-
items: opResult,
|
|
193
|
-
details: undefined,
|
|
194
|
-
disclosure: { level: "summary" },
|
|
195
|
-
};
|
|
196
|
-
const expectedText = standardListFmt.llm(output);
|
|
197
|
-
|
|
208
|
+
it("standard_list condensed lists every standard under the pack heading", async () => {
|
|
209
|
+
const allNames = await listDomainNames(rt.store, "standard");
|
|
198
210
|
const mcpRes = await client.callTool({
|
|
199
211
|
name: "standard_list",
|
|
200
212
|
arguments: { condensed: true },
|
|
201
213
|
});
|
|
202
214
|
const body = parseCondensed(mcpRes);
|
|
203
|
-
expect(body.text).
|
|
215
|
+
expect(body.text).toContain("## Standard");
|
|
216
|
+
for (const name of allNames) {
|
|
217
|
+
expect(body.text).toContain(name);
|
|
218
|
+
}
|
|
204
219
|
});
|
|
205
220
|
|
|
206
|
-
it("standard_lookup
|
|
207
|
-
const opResult = await lookupStandard(
|
|
208
|
-
rt.store,
|
|
209
|
-
"react/component/folder-structure",
|
|
210
|
-
);
|
|
221
|
+
it("standard_lookup returns full detail by default (MCP full-data contract)", async () => {
|
|
211
222
|
const mcpRes = await client.callTool({
|
|
212
223
|
name: "standard_lookup",
|
|
213
224
|
arguments: { names: ["react/component/folder-structure"] },
|
|
214
225
|
});
|
|
215
226
|
const body = parseEnvelope(mcpRes);
|
|
216
|
-
const data = body.data as {
|
|
217
|
-
|
|
227
|
+
const data = body.data as {
|
|
228
|
+
results: {
|
|
229
|
+
name: string;
|
|
230
|
+
category: string;
|
|
231
|
+
dos: { code?: string; language?: string }[];
|
|
232
|
+
donts: { code?: string }[];
|
|
233
|
+
}[];
|
|
234
|
+
errors: unknown[];
|
|
235
|
+
};
|
|
218
236
|
expect(data.errors).toEqual([]);
|
|
237
|
+
const entity = data.results[0];
|
|
238
|
+
expect(entity?.name).toBe("react/component/folder-structure");
|
|
239
|
+
expect(entity?.category).toBe("react");
|
|
240
|
+
expect(entity?.dos.length).toBeGreaterThan(0);
|
|
241
|
+
expect(entity?.dos.every((example) => example.code)).toBe(true);
|
|
242
|
+
expect(entity?.donts.length).toBeGreaterThan(0);
|
|
219
243
|
});
|
|
220
244
|
|
|
221
|
-
it("standard_lookup condensed
|
|
222
|
-
const opResult = await lookupStandard(
|
|
223
|
-
rt.store,
|
|
224
|
-
"react/component/folder-structure",
|
|
225
|
-
);
|
|
226
|
-
const expectedText = standardLookupFmt.llm({
|
|
227
|
-
standard: opResult,
|
|
228
|
-
detailed: true,
|
|
229
|
-
});
|
|
230
|
-
|
|
245
|
+
it("standard_lookup condensed renders the entity with its code examples", async () => {
|
|
231
246
|
const mcpRes = await client.callTool({
|
|
232
247
|
name: "standard_lookup",
|
|
233
248
|
arguments: {
|
|
@@ -236,28 +251,37 @@ describe("standard parity", () => {
|
|
|
236
251
|
},
|
|
237
252
|
});
|
|
238
253
|
const body = parseCondensed(mcpRes);
|
|
239
|
-
expect(body.text).
|
|
254
|
+
expect(body.text).toContain("## react/component/folder-structure");
|
|
255
|
+
expect(body.text).toContain("Do");
|
|
256
|
+
expect(body.text).toContain("src/lib/Button/Button.tsx");
|
|
240
257
|
});
|
|
241
258
|
|
|
242
|
-
it("standard_categories
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
name: "standard_categories",
|
|
259
|
+
it("standard_categories counts agree with the standard list", async () => {
|
|
260
|
+
const listRes = await client.callTool({
|
|
261
|
+
name: "standard_list",
|
|
246
262
|
arguments: {},
|
|
247
263
|
});
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
264
|
+
const rows = parseEnvelope(listRes).data as { category?: string }[];
|
|
265
|
+
const expected = new Map<string, number>();
|
|
266
|
+
for (const row of rows) {
|
|
267
|
+
if (row.category) {
|
|
268
|
+
expected.set(row.category, (expected.get(row.category) ?? 0) + 1);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
254
271
|
|
|
255
272
|
const mcpRes = await client.callTool({
|
|
256
273
|
name: "standard_categories",
|
|
257
|
-
arguments: {
|
|
274
|
+
arguments: {},
|
|
258
275
|
});
|
|
259
|
-
const body =
|
|
260
|
-
expect(body.
|
|
276
|
+
const body = parseEnvelope(mcpRes);
|
|
277
|
+
expect(body.ok).toBe(true);
|
|
278
|
+
const categories = body.data as { name: string; count: string }[];
|
|
279
|
+
expect(categories.length).toBeGreaterThan(0);
|
|
280
|
+
for (const [category, count] of expected) {
|
|
281
|
+
const row = categories.find((entry) => entry.name === category);
|
|
282
|
+
expect(row, `category "${category}" missing`).toBeDefined();
|
|
283
|
+
expect(Number(row?.count)).toBe(count);
|
|
284
|
+
}
|
|
261
285
|
});
|
|
262
286
|
});
|
|
263
287
|
|
|
@@ -265,26 +289,51 @@ describe("standard parity", () => {
|
|
|
265
289
|
// Tier parity
|
|
266
290
|
// ---------------------------------------------------------------------------
|
|
267
291
|
|
|
268
|
-
|
|
269
|
-
|
|
292
|
+
// `tier list` / `tier_list` is now served by the bundled `tier` story pack
|
|
293
|
+
// (the hand-written domain was deleted). Pack rows are `Record<string,string>`
|
|
294
|
+
// projected from the SELECT, so the shape is intentionally uniform: `{uri,
|
|
295
|
+
// name}` with no always-zero `depth` field, and the llm renderer is the
|
|
296
|
+
// generic pack renderer. These tests assert *semantic* parity — every tier the
|
|
297
|
+
// shared operation sees is present, by the same names — rather than byte
|
|
298
|
+
// equality with the removed bespoke formatter.
|
|
299
|
+
describe("tier parity (bundled pack)", () => {
|
|
300
|
+
it("tier_list returns one {uri, name} row per ontology tier", async () => {
|
|
270
301
|
const opResult = await listTiers(rt.store);
|
|
271
|
-
const mcpRes = await client.callTool({
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
302
|
+
const mcpRes = await client.callTool({ name: "tier_list", arguments: {} });
|
|
303
|
+
|
|
304
|
+
const content = mcpRes.content as { type: string; text: string }[];
|
|
305
|
+
const envelope = JSON.parse(content[0].text) as {
|
|
306
|
+
ok: boolean;
|
|
307
|
+
data: Array<{ uri?: string; name?: string }>;
|
|
308
|
+
};
|
|
309
|
+
expect(envelope.ok).toBe(true);
|
|
310
|
+
|
|
311
|
+
// Same tiers, same names — no tier dropped or invented by the cutover.
|
|
312
|
+
// The shared op names the field `path`; the pack names it `name` (the
|
|
313
|
+
// honest `ds:name` mapping) — the values are identical.
|
|
314
|
+
expect(new Set(envelope.data.map((r) => r.name))).toEqual(
|
|
315
|
+
new Set(opResult.map((t) => t.path)),
|
|
316
|
+
);
|
|
317
|
+
// Uniform pack shape: uri + name, no bespoke `depth` field.
|
|
318
|
+
for (const row of envelope.data) {
|
|
319
|
+
expect(row.uri).toBeTruthy();
|
|
320
|
+
expect(row).not.toHaveProperty("depth");
|
|
321
|
+
}
|
|
276
322
|
});
|
|
277
323
|
|
|
278
|
-
it("tier_list condensed
|
|
324
|
+
it("tier_list condensed lists every tier under a markdown heading", async () => {
|
|
279
325
|
const opResult = await listTiers(rt.store);
|
|
280
|
-
const expectedText = tierListFmt.llm(opResult);
|
|
281
|
-
|
|
282
326
|
const mcpRes = await client.callTool({
|
|
283
327
|
name: "tier_list",
|
|
284
328
|
arguments: { condensed: true },
|
|
285
329
|
});
|
|
286
330
|
const body = parseCondensed(mcpRes);
|
|
287
|
-
|
|
331
|
+
|
|
332
|
+
// The generic pack llm renderer headings by the capitalized noun: `## Tier`.
|
|
333
|
+
expect(body.text).toContain("## Tier");
|
|
334
|
+
for (const tier of opResult) {
|
|
335
|
+
expect(body.text).toContain(tier.path);
|
|
336
|
+
}
|
|
288
337
|
});
|
|
289
338
|
});
|
|
290
339
|
|
|
@@ -342,7 +391,7 @@ describe("modifier parity", () => {
|
|
|
342
391
|
// Token parity
|
|
343
392
|
// ---------------------------------------------------------------------------
|
|
344
393
|
|
|
345
|
-
describe("token parity", () => {
|
|
394
|
+
describe.skipIf(!TOKEN_READ_SURFACE_ENABLED)("token parity", () => {
|
|
346
395
|
it("token_list: count matches", async () => {
|
|
347
396
|
const opResult = await listTokens(rt.store);
|
|
348
397
|
const mcpRes = await client.callTool({
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Global test setup: isolate the XDG config
|
|
2
|
+
* Global test setup: isolate the XDG config and data layers.
|
|
3
3
|
*
|
|
4
4
|
* The layered config reader consults `$XDG_CONFIG_HOME/pragma/config.json`
|
|
5
|
-
* on every read, and global-first writes create it
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* on every read, and global-first writes create it; the schema artifact
|
|
6
|
+
* writes to `$XDG_DATA_HOME/pragma/schema.graphql`. Point both at
|
|
7
|
+
* per-run temp directories so tests neither observe nor pollute the
|
|
8
|
+
* developer's real global state. Individual tests that exercise XDG
|
|
9
|
+
* behavior override these values themselves.
|
|
10
|
+
*
|
|
11
|
+
* The boot-time schema artifact emission is disabled globally: it would
|
|
12
|
+
* add a full ke-graphql compile to every test that drives the real CLI
|
|
13
|
+
* pipeline and print an "Updated ..." notice on first boot. Its behavior
|
|
14
|
+
* is covered directly by ensureSchemaArtifact.test.ts; tests exercising
|
|
15
|
+
* the boot hook re-enable it explicitly.
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
18
|
import { mkdtempSync } from "node:fs";
|
|
@@ -13,3 +20,7 @@ import { tmpdir } from "node:os";
|
|
|
13
20
|
import { join } from "node:path";
|
|
14
21
|
|
|
15
22
|
process.env.XDG_CONFIG_HOME = mkdtempSync(join(tmpdir(), "pragma-test-xdg-"));
|
|
23
|
+
process.env.XDG_DATA_HOME = mkdtempSync(
|
|
24
|
+
join(tmpdir(), "pragma-test-xdg-data-"),
|
|
25
|
+
);
|
|
26
|
+
process.env.PRAGMA_SCHEMA_ARTIFACT = "off";
|
|
@@ -1,92 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Standard-noun parity
|
|
2
|
+
* Standard-noun cutover parity contract.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* must appear in {@link PARITY_GAPS} rather than being papered over.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { StoryPackDefinition } from "../domains/shared/stories/pack/types.js";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The built-in `standard` stories as a declarative pack definition.
|
|
4
|
+
* The hand-written `standard` domain was deleted; the noun is served by
|
|
5
|
+
* the bundled pack-v1 definition (`pack/bundled/standardPack.ts`), and
|
|
6
|
+
* `standardParity.test.ts` asserts SEMANTIC parity against the real
|
|
7
|
+
* @canonical/code-standards graphs: same entities and values reachable,
|
|
8
|
+
* uniform pack row/entity shapes, generic renderers.
|
|
16
9
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* lists what it cannot express.
|
|
10
|
+
* {@link PARITY_GAPS} is the honest record of the differences that
|
|
11
|
+
* remain BY DESIGN after the cutover. Each entry names a concrete,
|
|
12
|
+
* accepted divergence from the deleted built-in — remove an entry only
|
|
13
|
+
* when the pack format gains the capability and the parity test asserts
|
|
14
|
+
* it.
|
|
23
15
|
*/
|
|
24
|
-
export const STANDARD_PACK_STORY: StoryPackDefinition = {
|
|
25
|
-
noun: "standard",
|
|
26
|
-
description: "List all code standards",
|
|
27
|
-
list: {
|
|
28
|
-
query: [
|
|
29
|
-
"SELECT ?uri ?name ?category ?description",
|
|
30
|
-
"WHERE {",
|
|
31
|
-
" ?uri a cs:CodeStandard ;",
|
|
32
|
-
" cs:name ?name ;",
|
|
33
|
-
" cs:description ?description .",
|
|
34
|
-
" OPTIONAL {",
|
|
35
|
-
" ?uri cs:hasCategory ?cat .",
|
|
36
|
-
" ?cat cs:slug ?category .",
|
|
37
|
-
" }",
|
|
38
|
-
"}",
|
|
39
|
-
"ORDER BY ?name",
|
|
40
|
-
].join("\n"),
|
|
41
|
-
columns: [
|
|
42
|
-
{ field: "uri", label: "IRI" },
|
|
43
|
-
{ field: "name", label: "Name" },
|
|
44
|
-
{ field: "category", label: "Category" },
|
|
45
|
-
{ field: "description", label: "Description" },
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
lookup: {
|
|
49
|
-
type: "cs:CodeStandard",
|
|
50
|
-
by: "cs:name",
|
|
51
|
-
fields: [
|
|
52
|
-
{
|
|
53
|
-
name: "category",
|
|
54
|
-
property: "cs:hasCategory/cs:slug",
|
|
55
|
-
label: "Category",
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: "description",
|
|
59
|
-
property: "cs:description",
|
|
60
|
-
label: "Description",
|
|
61
|
-
},
|
|
62
|
-
{ name: "extends", property: "cs:extends", label: "Extends" },
|
|
63
|
-
],
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
16
|
|
|
67
17
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* gains the capability and the parity test asserts it.
|
|
18
|
+
* Accepted post-cutover divergences from the deleted built-in standard
|
|
19
|
+
* domain. These are doctrine (semantic parity, not byte parity) plus the
|
|
20
|
+
* capabilities pack v1 deliberately does not carry.
|
|
72
21
|
*/
|
|
73
22
|
export const PARITY_GAPS: readonly string[] = [
|
|
74
|
-
"list
|
|
75
|
-
"list
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"lookup
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"lookup by IRI: the built-in resolves an IRI or prefixed name (e.g. `cs:ComponentProps`) as the query; a pack lookup only matches the `by` property's string value, and only case-insensitively — exact-match semantics are not selectable",
|
|
87
|
-
"lookup recovery copy: built-in not-found errors carry cross-domain hints and the recovery message `List available standards.`; pack errors generate `List available standard entries.` — recovery copy is not authorable",
|
|
88
|
-
"lookup descriptions and examples: the pack lookup CLI description, tool description, names description, and examples are generated from the noun; the built-in curates all of them (e.g. `Look up detailed information for a standard by name or IRI`)",
|
|
89
|
-
"ink renderers: the built-in stories declare renderInk TUI views; the pack format has none",
|
|
90
|
-
"extra verbs: the standard noun also ships `categories` and `sample` commands; v0 packs compile only list and lookup",
|
|
91
|
-
"noun cutover: a pack story cannot register the noun `standard` while the built-in exists (reserved-noun guard in collectPackStories), so cutover must remove the built-in stories in the same release",
|
|
23
|
+
"list disclosure: pack lists are single-level — the old `standard list --digest/--detailed` row enrichment (extends/example/dos/donts merged into every row, disclosure meta on the MCP envelope) is gone; the same data is reachable per-entity via `standard lookup --detail digest|detailed` and `standard sample`",
|
|
24
|
+
"list empty guard: the old list raised EMPTY_RESULTS with an install-hint recovery when no standards load or a --category matched nothing; the pack format has no emptyError hook, so an empty or fully-filtered list renders zero rows successfully",
|
|
25
|
+
"lookup digest rendering: the old list digest truncated the first do-example to 120 chars; the pack `digest` level fetches the full `dos` expand instead (richer, not byte-equal)",
|
|
26
|
+
"lookup data compaction: the old domain compacted cs:extends in resolved data (JSON showed `cs:ComponentFolderStructure`); pack entities keep the raw IRI in JSON — plain/llm output still compacts at display time",
|
|
27
|
+
"lookup recovery copy: cross-domain hints (detectCrossDomain) and the curated `List available standards.` message are not authorable; pack misses carry ranked suggestions with the generic `List available standard entries.` copy",
|
|
28
|
+
'typed shapes: pack rows/entities are all-string records — category counts are strings, and dos/donts entries are `{caption?, language?, code?}` string records rather than typed CodeBlock objects (no `language: "typescript"` default, absent captions are omitted)',
|
|
29
|
+
"render templates: plain/llm output uses the generic pack renderers (aligned columns, `## Standard (n)` headings, `- key: value` collection entries), not the old bespoke standard templates",
|
|
30
|
+
"sample count flag: the pack sample takes a positional count (`standard sample 3`) instead of the old `--count 3` flag",
|
|
31
|
+
"lookup IRI display: plain/llm lookup output does not print the entity IRI (the pack renderer titles by name and projects no uri field); the IRI stays reachable via the list's IRI column, `--format json`, and the non-condensed MCP envelope",
|
|
32
|
+
"categories JSON shape: the old CategorySummary carried a numeric `standardCount`; pack category rows carry the key `count` with a string value (all-string pack rows, until pack v2 typed columns)",
|
|
33
|
+
"sample population: totalCount counts name-addressable standards (the set the sampler can actually resolve) rather than all list rows; the old op counted every list row but failed outright when it drew a standard without cs:name",
|
|
34
|
+
"ink renderers: the pack format declares no renderInk TUI views",
|
|
92
35
|
];
|
package/src/testing/store.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Test store bridge — createTestStore, PragmaTestStoreOptions.
|
|
3
3
|
*
|
|
4
4
|
* Wraps `@canonical/ke/testing` with pragma's default prefix map
|
|
5
|
-
* pre-configured so test authors do not need to repeat
|
|
5
|
+
* pre-configured so test authors do not need to repeat DEFAULT_PREFIX_MAP.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { TestStoreOptions, TestStoreResult } from "@canonical/ke/testing";
|
|
9
9
|
import { createTestStore as keCreateTestStore } from "@canonical/ke/testing";
|
|
10
|
-
import {
|
|
10
|
+
import { DEFAULT_PREFIX_MAP } from "../domains/shared/prefixes.js";
|
|
11
11
|
|
|
12
12
|
/** Options for creating a test store, with pragma prefixes pre-applied. */
|
|
13
13
|
interface PragmaTestStoreOptions extends Omit<TestStoreOptions, "prefixes"> {
|
|
@@ -18,7 +18,7 @@ interface PragmaTestStoreOptions extends Omit<TestStoreOptions, "prefixes"> {
|
|
|
18
18
|
* Create a test ke store with pragma's default prefix map.
|
|
19
19
|
*
|
|
20
20
|
* @param options - Optional overrides; additional prefixes are merged
|
|
21
|
-
* on top of the default
|
|
21
|
+
* on top of the default DEFAULT_PREFIX_MAP.
|
|
22
22
|
* @returns The test store result from ke/testing.
|
|
23
23
|
*
|
|
24
24
|
* @note Impure
|
|
@@ -29,7 +29,7 @@ async function createTestStore(
|
|
|
29
29
|
return keCreateTestStore({
|
|
30
30
|
...options,
|
|
31
31
|
prefixes: {
|
|
32
|
-
...
|
|
32
|
+
...DEFAULT_PREFIX_MAP,
|
|
33
33
|
...options.prefixes,
|
|
34
34
|
},
|
|
35
35
|
});
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import type { CommandContext, CommandDefinition } from "@canonical/cli-core";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { PragmaError } from "#error";
|
|
4
|
-
import buildComponentCommand from "./component.js";
|
|
5
|
-
|
|
6
|
-
const ctx: CommandContext = {
|
|
7
|
-
cwd: "/tmp",
|
|
8
|
-
globalFlags: { llm: false, format: "text" as const, verbose: false },
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const llmCtx: CommandContext = {
|
|
12
|
-
cwd: "/tmp",
|
|
13
|
-
globalFlags: { llm: true, format: "text" as const, verbose: false },
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const jsonCtx: CommandContext = {
|
|
17
|
-
cwd: "/tmp",
|
|
18
|
-
globalFlags: { llm: false, format: "json" as const, verbose: false },
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
async function executeOutput(
|
|
22
|
-
cmd: CommandDefinition,
|
|
23
|
-
params: Record<string, unknown>,
|
|
24
|
-
context: CommandContext,
|
|
25
|
-
): Promise<{ value: unknown; text: string }> {
|
|
26
|
-
const result = await cmd.execute(params, context);
|
|
27
|
-
expect(result.tag).toBe("output");
|
|
28
|
-
if (result.tag !== "output") throw new Error("Expected output result");
|
|
29
|
-
const text = result.render.plain(result.value);
|
|
30
|
-
return { value: result.value, text };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
describe("buildComponentCommand", () => {
|
|
34
|
-
it("has correct path", () => {
|
|
35
|
-
const cmd = buildComponentCommand();
|
|
36
|
-
expect(cmd.path).toEqual(["create", "component"]);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("includes framework as positional required select parameter", () => {
|
|
40
|
-
const cmd = buildComponentCommand();
|
|
41
|
-
const framework = cmd.parameters.find((p) => p.name === "framework");
|
|
42
|
-
expect(framework).toBeDefined();
|
|
43
|
-
expect(framework?.positional).toBe(true);
|
|
44
|
-
expect(framework?.required).toBe(true);
|
|
45
|
-
expect(framework?.type).toBe("select");
|
|
46
|
-
expect(framework?.choices?.map((c) => c.value)).toEqual([
|
|
47
|
-
"react",
|
|
48
|
-
"svelte",
|
|
49
|
-
"lit",
|
|
50
|
-
]);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("includes prompt-derived parameters", () => {
|
|
54
|
-
const cmd = buildComponentCommand();
|
|
55
|
-
const names = cmd.parameters.map((p) => p.name);
|
|
56
|
-
expect(names).toContain("componentPath");
|
|
57
|
-
expect(names).toContain("withStyles");
|
|
58
|
-
expect(names).toContain("withStories");
|
|
59
|
-
expect(names).toContain("withSsrTests");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("includes execution-mode parameters", () => {
|
|
63
|
-
const cmd = buildComponentCommand();
|
|
64
|
-
const names = cmd.parameters.map((p) => p.name);
|
|
65
|
-
expect(names).toContain("dryRun");
|
|
66
|
-
expect(names).toContain("yes");
|
|
67
|
-
expect(names).toContain("showFiles");
|
|
68
|
-
expect(names).toContain("preview");
|
|
69
|
-
expect(names).toContain("generatedStamp");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("dry-run with react returns output with file paths", async () => {
|
|
73
|
-
const cmd = buildComponentCommand();
|
|
74
|
-
const { text } = await executeOutput(
|
|
75
|
-
cmd,
|
|
76
|
-
{
|
|
77
|
-
framework: "react",
|
|
78
|
-
componentPath: "src/components/Button",
|
|
79
|
-
dryRun: true,
|
|
80
|
-
yes: true,
|
|
81
|
-
},
|
|
82
|
-
ctx,
|
|
83
|
-
);
|
|
84
|
-
expect(text).toContain("Button");
|
|
85
|
-
expect(text).toContain("src/components/Button");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("dry-run with svelte returns output with svelte files", async () => {
|
|
89
|
-
const cmd = buildComponentCommand();
|
|
90
|
-
const { text } = await executeOutput(
|
|
91
|
-
cmd,
|
|
92
|
-
{
|
|
93
|
-
framework: "svelte",
|
|
94
|
-
componentPath: "src/lib/components/Toggle",
|
|
95
|
-
dryRun: true,
|
|
96
|
-
yes: true,
|
|
97
|
-
},
|
|
98
|
-
ctx,
|
|
99
|
-
);
|
|
100
|
-
expect(text).toContain("Toggle");
|
|
101
|
-
expect(text).toContain("src/lib/components/Toggle");
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("LLM mode returns markdown", async () => {
|
|
105
|
-
const cmd = buildComponentCommand();
|
|
106
|
-
const { text } = await executeOutput(
|
|
107
|
-
cmd,
|
|
108
|
-
{
|
|
109
|
-
framework: "react",
|
|
110
|
-
componentPath: "src/components/Card",
|
|
111
|
-
},
|
|
112
|
-
llmCtx,
|
|
113
|
-
);
|
|
114
|
-
// LLM markdown output includes generator name header
|
|
115
|
-
expect(text).toContain("component/react");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("JSON mode returns parseable JSON", async () => {
|
|
119
|
-
const cmd = buildComponentCommand();
|
|
120
|
-
const { text } = await executeOutput(
|
|
121
|
-
cmd,
|
|
122
|
-
{
|
|
123
|
-
framework: "react",
|
|
124
|
-
componentPath: "src/components/Card",
|
|
125
|
-
},
|
|
126
|
-
jsonCtx,
|
|
127
|
-
);
|
|
128
|
-
const data = JSON.parse(text);
|
|
129
|
-
expect(data.generator.name).toBe("component/react");
|
|
130
|
-
expect(Array.isArray(data.plan)).toBe(true);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("throws PragmaError for invalid framework", async () => {
|
|
134
|
-
const cmd = buildComponentCommand();
|
|
135
|
-
await expect(cmd.execute({ framework: "angular" }, ctx)).rejects.toThrow(
|
|
136
|
-
PragmaError,
|
|
137
|
-
);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("throws PragmaError when framework is missing", async () => {
|
|
141
|
-
const cmd = buildComponentCommand();
|
|
142
|
-
await expect(cmd.execute({}, ctx)).rejects.toThrow(PragmaError);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("returns interactive result when required answers are missing", async () => {
|
|
146
|
-
const cmd = buildComponentCommand();
|
|
147
|
-
// framework provided but componentPath has a default, so it should NOT
|
|
148
|
-
// trigger interactive mode — all prompts have defaults
|
|
149
|
-
const result = await cmd.execute({ framework: "lit", dryRun: true }, ctx);
|
|
150
|
-
// componentPath has a default, so executeGenerator applies it and proceeds
|
|
151
|
-
expect(result.tag).toBe("output");
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("has parameter groups", () => {
|
|
155
|
-
const cmd = buildComponentCommand();
|
|
156
|
-
expect(cmd.parameterGroups).toEqual({
|
|
157
|
-
Component: ["componentPath"],
|
|
158
|
-
Options: ["withStyles", "withStories", "withSsrTests"],
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it("has examples in meta", () => {
|
|
163
|
-
const cmd = buildComponentCommand();
|
|
164
|
-
expect(cmd.meta?.examples?.length).toBeGreaterThan(0);
|
|
165
|
-
});
|
|
166
|
-
});
|