diff --git a/packages/core/src/loop/boringstack/acceptance/e2e-generator.ts b/packages/core/src/loop/boringstack/acceptance/e2e-generator.ts index 41999cad..ed7eef2c 100644 --- a/packages/core/src/loop/boringstack/acceptance/e2e-generator.ts +++ b/packages/core/src/loop/boringstack/acceptance/e2e-generator.ts @@ -175,7 +175,11 @@ function emitSpecParentSeed( function generateParentSeedingCode( parents: IParentRef[], entityId: string, - spec?: IAcceptanceSpec + spec?: IAcceptanceSpec, + // Prepend the "seed before navigate" rationale. Only the form-driven tests navigate to the + // child page and select from its async note would be misleading there → opt-out. + withSelectNote = true ): string { if (parents.length === 0) { return ""; @@ -224,7 +228,22 @@ function generateParentSeedingCode( emitParentSeeding(parent.key, parent.entity, 0); } - return codeBlocks.join("\n"); + if (codeBlocks.length === 0) { + return ""; + } + + if (!withSelectNote) { + return codeBlocks.join("\n"); + } + + // Callers place this BEFORE navigating to the child page: the child's parent lists them."; + + return [note, ...codeBlocks].join("\n"); } /** @@ -367,8 +386,14 @@ function generateFieldFillSteps( if (parent) { const varName = `${parent.key}Id`; - - return ` await page.getByTestId("${ids.field(field.name)}").selectOption(${varName});`; + const sel = `page.getByTestId("${ids.field(field.name)}")`; + + // The parent ), so seed without the + // form-oriented " ordering (Contact belongsTo Company)", () => { + // A child entity whose form has a company parent-FK rendered as a native ). + const contact: IEntityAcceptance = { + id: "Contact", + key: "contact", + nav: "Contacts", + fields: [ + { + name: "name", + type: "string", + optional: false, + valid: "Jane", + invalid: [], + }, + { + name: "companyId", + type: "string", + optional: false, + valid: "", + invalid: [], + }, + ], + shows: ["name", "company"], + screens: ["list", "form"], + parents: [{ entity: "Company", key: "company", fkField: "companyId" }], + negatives: [{ field: "name", value: "", why: "name is required" }], + acceptanceCheck: "create a contact under a company", + }; + + // Extract one test block by a substring of its title, up to the next top-level ` test(`. + const extractBlock = (spec: string, titleMarker: string): string => { + const start = spec.indexOf(titleMarker); + + if (start === -1) { + return ""; + } + + const rest = spec.slice(start); + const next = rest.indexOf("\n test(", 1); + + return next === -1 ? rest : rest.slice(0, next); + }; + + // Every test that navigates to the child page and fills its parent-FK rationale note appears in form-driven tests but NOT in the API-only negative block (which still seeds the parent)", () => { + const spec = generateEntitySpec(contact); + const note = "async parent-). + expect(spec).toContain(note); + + // The negative block POSTs directly (no navigation, no note. + const negIdx = spec.indexOf("negative: Contact rejects"); + + expect(negIdx).toBeGreaterThan(-1); + + const negBlock = spec.slice(negIdx); + + expect(negBlock).toContain("/api/v1/company"); // still seeds the parent for the FK payload + expect(negBlock).not.toContain(note); // but no misleading wait/selection — the fix is scoped to relational children", () => { + const spec = generateEntitySpec(company); // company has parents: [] + + expect(spec).not.toContain('option[value="${'); // no FK option-attached wait + expect(spec).not.toContain(".selectOption("); // no parent-FK selection at all + }); +});