diff --git a/packages/lint/src/rules/media.test.ts b/packages/lint/src/rules/media.test.ts index 0fdee1bed..8237efa0d 100644 --- a/packages/lint/src/rules/media.test.ts +++ b/packages/lint/src/rules/media.test.ts @@ -566,8 +566,12 @@ describe("audio_group_no_members", () => { const BUS = ``; - it("errors on a bus no clip belongs to", async () => { - const res = await lintHyperframeHtml(doc(BUS)); + it("errors on a bus no clip in the file belongs to", async () => { + const res = await lintHyperframeHtml( + doc( + `${BUS}`, + ), + ); const finding = res.findings.find((f) => f.code === "audio_group_no_members"); expect(finding?.severity).toBe("error"); expect(finding?.elementId).toBe("voiceover"); @@ -597,6 +601,20 @@ describe("audio_group_no_members", () => { // A bus with no id cannot be joined at all, and `resolveAudioGroups` skips it // when building its element map — a different mistake, not this rule's. + // The rule can only speak about a file it can see all of. `lintHyperframeHtml` + // takes ONE file, and the studio's own group creation writes the bus into the + // active composition while patching `data-audio-group` into each member's own + // file (timelineAudioGroupCreate) — so a file holding a bus and no members at + // all is the normal cross-file shape, not a mistake. + it("stays quiet in a file that declares no members at all", async () => { + const res = await lintHyperframeHtml( + doc( + `${BUS}
`, + ), + ); + expect(res.findings.some((f) => f.code === "audio_group_no_members")).toBe(false); + }); + it("stays quiet for a bus with no id", async () => { const res = await lintHyperframeHtml( doc(``), diff --git a/packages/lint/src/rules/media.ts b/packages/lint/src/rules/media.ts index cd1396dc7..cb0576ce9 100644 --- a/packages/lint/src/rules/media.ts +++ b/packages/lint/src/rules/media.ts @@ -800,6 +800,15 @@ function findAudioGroupNoMembersFindings(ctx: LintContext): HyperframeLintFindin .filter((id): id is string => Boolean(id)), ); + // Only a file that declares SOME membership can be judged. `lintHyperframeHtml` + // sees one file, and the studio's own group creation writes the bus into the + // active composition while patching `data-audio-group` into each member's own + // file (`timelineAudioGroupCreate`) — so a file carrying a bus and no members + // at all is the ordinary cross-file shape. Firing there reported the studio's + // own output as an error, and said "No clip carries `data-audio-group` at all" + // about clips it simply could not see. + if (memberGroupIds.size === 0) return []; + const findings: HyperframeLintFinding[] = []; for (const tag of ctx.tags) { if (tag.name !== "hf-audio-group") continue; @@ -814,8 +823,8 @@ function findAudioGroupNoMembersFindings(ctx: LintContext): HyperframeLintFindin const nearby = [...memberGroupIds].filter((id) => id !== elementId); const suffix = nearby.length > 0 - ? ` Clips name ${nearby.map((id) => `"${id}"`).join(", ")} instead.` - : " No clip carries `data-audio-group` at all."; + ? ` Clips in this file name ${nearby.map((id) => `"${id}"`).join(", ")} instead.` + : ""; findings.push({ code: "audio_group_no_members", severity: "error",