test(editor): repair two specs the removed value path left behind
Both were fair tests until the value round trip was deleted. They drove a VALUE
context and asserted the values arrived through `keywords` while `suggestions`
went blank -- which was the only way to tell effectiveKeywords from
autoCompleteKeywords, since the two are identical unless a context is active.
The provider now resolves values itself, so nothing pushes them through props.
Replaced, not deleted:
- the N1 invariant they guarded (bind the context-aware list, never the base
one) is enforced for EVERY editor host in editorWiring.spec.ts, which needs
no context to make the distinction visible;
- what this file can still prove is its own wiring: the resolver reaches the
editor and resolves against the stream context the dialog sets. Verified
non-vacuous -- removing the :field-value-resolver binding fails it.
- "blanks the suggestions" is now the OPPOSITE assertion, because blanking
them was part of the removed round trip: the catalog must survive.
Found by an external review, not by me, because I ran only the specs I had
touched. The full suite also surfaced a second miss: sourceHygiene.spec.ts, a
guard this repo already had, was failing on three RAW C0 control characters I
had committed in sortText fixtures -- \x01 in editorProviders.spec.ts and \x02
twice in sqlCompletion.spec.ts, now written as escapes. That is the third time
today a raw control character got into source from my editing path; the repo has
a test for it and I was not running it.
Full suite: 40367 total, 39935 passed, 24 failed -- 9 load-flaky
CodeQueryEditor and 15 synthetics journey specs, all confirmed pre-existing.
Zero related to this workstream, which was not true before this commit.
This commit is contained in:
parent
449e2d2dda
commit
ff89c82c08
|
|
@ -541,20 +541,27 @@ describe("QueryEditorDialog - ODrawer Migration", () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ─── Phase 1 (tmp/code.md N1) ─────────────────────────────────────────────────
|
||||
// QueryEditorDialog wires the whole autocomplete pipeline in handleQueryUpdate
|
||||
// (query, cursorIndex, org/stream context, popup.open, getSuggestions) but binds
|
||||
// :keywords="autoCompleteKeywords" — the BASE list — instead of effectiveKeywords.
|
||||
// ─── Phase 1 (N1) / Phase 3 (C4) — autocomplete reaches the editor ───────────
|
||||
// These two tests used to drive a VALUE context and assert that values arrived
|
||||
// through `keywords` while `suggestions` went blank. That was the only way to
|
||||
// tell effectiveKeywords from autoCompleteKeywords, which are identical unless
|
||||
// a context is active — and it was a fair test until the value round trip was
|
||||
// removed: the completion PROVIDER now resolves values itself, so nothing
|
||||
// pushes them through the props any more.
|
||||
//
|
||||
// Comparing the bound prop against vm.effectiveKeywords would pass even with the
|
||||
// bug, because effectiveKeywords returns autoCompleteKeywords verbatim when no
|
||||
// context is active. Drive a real VALUE context instead.
|
||||
// What replaces each half:
|
||||
// - the N1 invariant (bind the context-aware list, never the base one) is now
|
||||
// enforced for EVERY editor host in utils/query/editorWiring.spec.ts, which
|
||||
// needs no context to make the distinction visible;
|
||||
// - what this file can still prove is its own wiring — that the resolver the
|
||||
// provider awaits arrives, and resolves against the stream context the
|
||||
// dialog sets.
|
||||
|
||||
describe("QueryEditorDialog - N1 context keywords reach the editor", () => {
|
||||
describe("QueryEditorDialog - autocomplete wiring reaches the editor", () => {
|
||||
const editorStubDef = {
|
||||
name: "UnifiedQueryEditor",
|
||||
template: '<div class="stub-kw-editor" />',
|
||||
props: ["query", "keywords", "suggestions"],
|
||||
props: ["query", "keywords", "suggestions", "fieldValueResolver"],
|
||||
emits: ["update:query", "blur", "focus", "language-change", "ask-ai", "run-query"],
|
||||
methods: {
|
||||
// handleQueryUpdate reads these off queryEditorRef.
|
||||
|
|
@ -594,8 +601,7 @@ describe("QueryEditorDialog - N1 context keywords reach the editor", () => {
|
|||
name: "ODrawer",
|
||||
props: ["open", "size", "showClose", "bleed", "persistent", "title", "width"],
|
||||
emits: ["update:open"],
|
||||
template:
|
||||
"<div><slot name='header-left' /><slot name='header-right' /><slot /></div>",
|
||||
template: "<div><slot name='header-left' /><slot name='header-right' /><slot /></div>",
|
||||
},
|
||||
FullViewContainer: {
|
||||
template: "<div><slot /><slot name='right' /></div>",
|
||||
|
|
@ -612,27 +618,35 @@ describe("QueryEditorDialog - N1 context keywords reach the editor", () => {
|
|||
expect(wrapper.findComponent({ name: "UnifiedQueryEditor" }).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("delivers field VALUES to the editor once the cursor is after an operator", async () => {
|
||||
it("hands the editor a resolver that produces the stored values", async () => {
|
||||
const wrapper = mountWithStub();
|
||||
const editor = wrapper.findComponent({ name: "UnifiedQueryEditor" });
|
||||
// Drives handleQueryUpdate, which is where the dialog sets the org/stream
|
||||
// context the lookup is keyed on. Without that the resolver returns [] and
|
||||
// this fails — which is the wiring worth guarding.
|
||||
await editor.vm.$emit("update:query", "level = ");
|
||||
await flushPromises();
|
||||
|
||||
const delivered = (editor.props("keywords") ?? []) as any[];
|
||||
expect(delivered.some((k) => k.kind === "Value")).toBe(true);
|
||||
const resolve = editor.props("fieldValueResolver") as (f: string) => Promise<string[]>;
|
||||
expect(typeof resolve, "no resolver reached the editor").toBe("function");
|
||||
await expect(resolve("level")).resolves.toEqual(expect.arrayContaining(["error", "warn"]));
|
||||
});
|
||||
|
||||
it("blanks the function suggestions while in value context", async () => {
|
||||
it("leaves the catalog suggestions alone in a value position", async () => {
|
||||
// The provider swaps the whole list out for values itself. The parent
|
||||
// blanking the catalog was part of the removed round trip, and doing it
|
||||
// here would now take the functions away for no one's benefit.
|
||||
const wrapper = mountWithStub();
|
||||
const editor = wrapper.findComponent({ name: "UnifiedQueryEditor" });
|
||||
await editor.vm.$emit("update:query", "level = ");
|
||||
await flushPromises();
|
||||
|
||||
expect(editor.props("suggestions")).toEqual([]);
|
||||
const suggestions = (editor.props("suggestions") ?? []) as any[];
|
||||
expect(suggestions.some((s: any) => s.name === "match_all")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// Stored field values for the N1 value-context probe above. Only useSuggestions
|
||||
// Stored field values for the resolver test above. Only useSuggestions
|
||||
// consumes this module, so mocking it does not affect the rest of the suite.
|
||||
vi.mock("@/composables/fieldValueStore", () => ({
|
||||
getFieldValuesForSuggestion: vi.fn().mockResolvedValue(["error", "warn"]),
|
||||
|
|
|
|||
|
|
@ -528,7 +528,7 @@ describe("rankNumericFieldsFirst", () => {
|
|||
},
|
||||
{ label: "value", kind: "Field", detail: "Float64", sortText: `${FIELD_LANE}value` },
|
||||
{ label: "duration_ms", kind: "Field", detail: "Int64", sortText: `${FIELD_LANE}duration_ms` },
|
||||
{ label: "avg", kind: "Function", detail: "(field)", sortText: "avg" },
|
||||
{ label: "avg", kind: "Function", detail: "(field)", sortText: "\u0001avg" },
|
||||
] as any[];
|
||||
|
||||
// localeCompare ignores the control characters the lanes are built from, so
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ describe("N7 — optional metadata is forwarded to monaco", () => {
|
|||
documentation: "Approximate top-k aggregation.",
|
||||
insertText: "approx_topk(${1:field}, ${2:10})",
|
||||
insertTextRules: "InsertAsSnippet",
|
||||
sortText: "approx_topk",
|
||||
sortText: "\u0002approx_topk",
|
||||
};
|
||||
|
||||
it("forwards detail", () => {
|
||||
|
|
@ -347,7 +347,7 @@ describe("N7 — optional metadata is forwarded to monaco", () => {
|
|||
});
|
||||
|
||||
it("forwards sortText", () => {
|
||||
expect(build({ suggestions: [rich] })[0].sortText).toBe("approx_topk");
|
||||
expect(build({ suggestions: [rich] })[0].sortText).toBe("\u0002approx_topk");
|
||||
});
|
||||
|
||||
it("attaches the supplied range to every item", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue