From 06650ddb38540d358b4772c5e82654b427526eef Mon Sep 17 00:00:00 2001 From: Dotta Date: Wed, 4 Nov 2026 16:35:07 -0600 Subject: [PATCH] test(evals): follow actual Files tab before skill Edit Co-Authored-By: Paperclip --- tests/runner-e2e/CHAT-FEATURES.md | 2 +- .../integrator-skill-authoring-v2.test.ts | 9 +++++---- .../chat-catalog-domains/integrator-skills.ts | 1 + ui/src/pages/CompanySkills.test.tsx | 11 +++++++++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/runner-e2e/CHAT-FEATURES.md b/tests/runner-e2e/CHAT-FEATURES.md index 33b322eedb2..15240d235a7 100644 --- a/tests/runner-e2e/CHAT-FEATURES.md +++ b/tests/runner-e2e/CHAT-FEATURES.md @@ -243,7 +243,7 @@ Only `steering.preserve-draft` and `tasks.attachment-brief` may currently declar `skills.author-skill` v3 follows the normal library card to the exact saved skill. It accepts the canonical company-scoped detail route or the exact Studio route. -The detail view must expose its normal Edit control. Both paths must show the +The detail view opens Overview first; select Files, then its normal Edit control. Both paths must show the editable saved checklist, survive reload, and match the same public current version. The receipt identifies the actual editor surface. The original case clock, query, and one-human/one-native limits remain unchanged. Older failed diff --git a/tests/runner-e2e/chat-catalog-domains/integrator-skill-authoring-v2.test.ts b/tests/runner-e2e/chat-catalog-domains/integrator-skill-authoring-v2.test.ts index 970cc976b30..414d57ab50b 100644 --- a/tests/runner-e2e/chat-catalog-domains/integrator-skill-authoring-v2.test.ts +++ b/tests/runner-e2e/chat-catalog-domains/integrator-skill-authoring-v2.test.ts @@ -12,14 +12,15 @@ function fixture(){ const skill={id:"skill",companyId:"company",name:"Website review checklist",slug:"website-review-checklist",editable:true,currentVersionId:"revision",markdown,usedByAgents:[]}; const state:Record={},o={scenarioId:"skills.author-skill",origin:"https://fixture.test",companyId:"company",leadId:"ceo",state,skills:[skill],versions:{skill:[version]},policy:{},editDecision:{allowed:true},tasks:[],projects:[],goals:[],comments:[],documents:[],revisions:[],humanQueries:["Turn our website review checklist into a reusable skill."],runs:[{id:"native",status:"succeeded",agentId:"ceo",companyId:"company"}],activity:[{action:"company.skill_created",entityId:"skill",agentId:"ceo"}],links:[],reply:"Created the reusable company skill, saved in the library and not assigned to any agent."} as unknown as InternalObservation; observed.base=o; - const ui={library:true,studio:true,editable:true,body:markdown,changedAfterReload:false,wrongRoute:false,foreignRoute:false,directStudio:false,delayedHydration:false,inEdit:false};const events:string[]=[]; + const ui={library:true,studio:true,editable:true,body:markdown,changedAfterReload:false,wrongRoute:false,foreignRoute:false,directStudio:false,delayedHydration:false,inEdit:false,filesSelected:false};const events:string[]=[]; const editor={get visible(){return ui.studio && (ui.inEdit || ui.directStudio);},get editable(){return ui.editable;},locator:()=>({evaluateAll:async()=>ui.delayedHydration&&editorReads++===0?[]:(ui.changedAfterReload&&reloads?"Lost checklist":ui.body).split("\n")})}; const page={url:()=>current,getByRole:(role:string,options?:{name?:string})=>{ if(role==="textbox")return editor; - if(role==="button" && options?.name==="Edit")return{click:async()=>{if(!ui.editable)throw Error("No Edit control");ui.inEdit=true;events.push("Edit");}}; + if(role==="tab" && options?.name==="Files")return{click:async()=>{ui.filesSelected=true;events.push("Files");}}; + if(role==="button" && options?.name==="Edit")return{click:async()=>{if(!ui.editable||!ui.filesSelected)throw Error("No Edit control outside Files");ui.inEdit=true;events.push("Edit");}}; if(role==="link")return{first:()=>({click:async()=>{events.push(options!.name!);current=options?.name==="Skills"?"https://fixture.test/ACORN/skills":"https://fixture.test/ACORN/skills/studio";}})}; return{filter:()=>({first:()=>({get visible(){return ui.library;},click:async()=>{events.push("open-skill");current=ui.foreignRoute?"https://fixture.test/FOREIGN/skills/website-review-checklist":ui.wrongRoute?"https://fixture.test/ACORN/skills/other":ui.directStudio?"https://fixture.test/ACORN/skills/studio/skill":"https://fixture.test/ACORN/skills/website-review-checklist";}})})}; - },getByText:()=>({first:()=>({get visible(){return ui.studio;}})}),getByTestId:()=>({get visible(){return current===source;}}),reload:async()=>{reloads++;ui.inEdit=false;events.push("reload");},goto:async(url:string)=>{current=url;events.push("return-chat");}}; + },getByText:()=>({first:()=>({get visible(){return ui.studio;}})}),getByTestId:()=>({get visible(){return current===source;}}),reload:async()=>{reloads++;ui.inEdit=false;ui.filesSelected=false;events.push("reload");},goto:async(url:string)=>{current=url;events.push("return-chat");}}; const after=structuredClone(skill),get=vi.fn(async(route:string)=>{ if(route.endsWith("/skills"))return[{id:"skill"}]; if(route.endsWith("/skills/skill"))return after; @@ -34,7 +35,7 @@ function fixture(){ it("the actual authoring act performs nonmutating canonical-library/Edit inspection and keeps a single native/human turn",async()=>{ const f=fixture(),d=integratorSkillsCases.find(x=>x.scenarioId==="skills.author-skill")!; await d.act!(f.ctx,f.state); - expect(f.events).toEqual(["Skills","My Skills","open-skill","Edit","reload","Edit","return-chat"]); + expect(f.events).toEqual(["Skills","My Skills","open-skill","Files","Edit","reload","Files","Edit","return-chat"]); expect(f.state.authoredSkillInspection).toMatchObject({skillId:"skill",versionId:"revision",editorSurface:"detail",editorVisible:true,readbackMatched:true}); expect(gradeIntegratorSkills({...f.o,state:f.state}).passed).toBe(true); expect(d.expectedRunCount).toBe(1);expect(d.expectedHumanMessageCount).toBe(1); diff --git a/tests/runner-e2e/chat-catalog-domains/integrator-skills.ts b/tests/runner-e2e/chat-catalog-domains/integrator-skills.ts index 5f34b73f8b1..05af772d071 100644 --- a/tests/runner-e2e/chat-catalog-domains/integrator-skills.ts +++ b/tests/runner-e2e/chat-catalog-domains/integrator-skills.ts @@ -119,6 +119,7 @@ export async function inspectAuthoredSkill(ctx: ChatCatalogContext, state: Recor await expect(ctx.input.page).toHaveURL(url => url.origin === new URL(source).origin && (url.pathname === studioRoute || url.pathname === detailRoute), { timeout: remaining() }); if (new URL(ctx.input.page.url()).pathname === detailRoute) { + await ctx.input.page.getByRole("tab", { name: "Files", exact: true }).click({ timeout: remaining() }); await ctx.input.page.getByRole("button", { name: "Edit", exact: true }).click({ timeout: remaining() }); } }; diff --git a/ui/src/pages/CompanySkills.test.tsx b/ui/src/pages/CompanySkills.test.tsx index e3ab96ed8f0..234cc3946c1 100644 --- a/ui/src/pages/CompanySkills.test.tsx +++ b/ui/src/pages/CompanySkills.test.tsx @@ -1154,3 +1154,14 @@ describe("install-time agent enablement", () => { expect(onConfirm).toHaveBeenCalledWith({ slug: "wireframe", force: false, agentIds: [] }); }); }); + +it.each(["overview", "files"] as const)("exposes the real named Edit control only in the %s tab", async activeTab => { + const version = makeVersion(1, "# Demo skill\n\nSaved body"); + const setEditMode = vi.fn(); + const node = await renderSkillDetail([version], { activeTab, setEditMode, + file: { skillId: "skill-1", path: "SKILL.md", kind: "skill", content: "# Demo skill\n\nSaved body", language: "markdown", markdown: true, editable: true, encoding: "utf8" } }); + expect(buttonsNamed(node, "Files")).toHaveLength(1); + const edit = buttonsNamed(node, "Edit"); + expect(edit).toHaveLength(activeTab === "files" ? 1 : 0); + if (activeTab === "files") { await click(edit[0]!); expect(setEditMode).toHaveBeenCalledExactlyOnceWith(true); } +});