diff --git a/packages/testing/janitor/src/core/test-discovery-analyzer.test.ts b/packages/testing/janitor/src/core/test-discovery-analyzer.test.ts index caccf2f2d5e..8407e06d6f7 100644 --- a/packages/testing/janitor/src/core/test-discovery-analyzer.test.ts +++ b/packages/testing/janitor/src/core/test-discovery-analyzer.test.ts @@ -69,6 +69,21 @@ describe('TestDiscoveryAnalyzer', () => { expect(report.specs.map((s) => s.path)).toEqual(['tests/a.spec.ts', 'tests/b.spec.ts']); }); + + it('discovers tests whose titles are template literals with substitutions', () => { + const file = createFile( + 'tests/dynamic.spec.ts', + ` +for (const path of ['a', 'b']) { + test(\`handles \${path}\`, async () => {}); +} +`, + ); + const report = discoverWith([file]); + + expect(report.specs).toHaveLength(1); + expect(report.specs[0].path).toBe('tests/dynamic.spec.ts'); + }); }); describe('skip and fixme detection', () => { diff --git a/packages/testing/janitor/src/core/test-discovery-analyzer.ts b/packages/testing/janitor/src/core/test-discovery-analyzer.ts index e254911d9f0..295fe92ea6c 100644 --- a/packages/testing/janitor/src/core/test-discovery-analyzer.ts +++ b/packages/testing/janitor/src/core/test-discovery-analyzer.ts @@ -199,7 +199,10 @@ export class TestDiscoveryAnalyzer { /** * Extract the title string from the first argument of a test/describe call. - * Returns null for calls without a string title (e.g., test.fixme() with no args). + * Returns null for calls without a title argument (e.g., test.fixme() with no args). + * Dynamic titles (template literals with substitutions, concatenation, identifiers) + * return the raw source text so the call still counts as a real test and tags inside + * the static portions can be extracted. */ private extractTitle(call: CallExpression): string | null { const args = call.getArguments(); @@ -207,14 +210,13 @@ export class TestDiscoveryAnalyzer { const firstArg = args[0]; - // Handle string literals: 'title', "title", `title` const asString = firstArg.asKind(SyntaxKind.StringLiteral); if (asString) return asString.getLiteralText(); const asTemplate = firstArg.asKind(SyntaxKind.NoSubstitutionTemplateLiteral); if (asTemplate) return asTemplate.getLiteralText(); - return null; + return firstArg.getText(); } private parseTags(title: string): string[] { diff --git a/packages/testing/playwright/tests/e2e/api/webhook-isolation.spec.ts b/packages/testing/playwright/tests/e2e/api/webhook-isolation.spec.ts index c602fc04c9f..babe606ede7 100644 --- a/packages/testing/playwright/tests/e2e/api/webhook-isolation.spec.ts +++ b/packages/testing/playwright/tests/e2e/api/webhook-isolation.spec.ts @@ -32,7 +32,7 @@ test.describe( ]; const expectedCSP = - 'sandbox allow-downloads allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-presentation allow-scripts allow-top-navigation-by-user-activation allow-top-navigation-to-custom-protocols'; + 'sandbox allow-downloads allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-scripts allow-top-navigation-by-user-activation allow-top-navigation-to-custom-protocols'; for (const webhookPath of webhookPaths) { test(`Webhook responses should include the correct response headers for ${webhookPath}`, async ({