Skip to content

Commit 6d9705f

Browse files
yuyutaotaoottomao
andauthored
refactor(core): deprecate testId fallback and harden reportFileName (#2264)
* refactor(core): deprecate testId fallback and harden reportFileName * fix(cli): align CDP tests with reportFileName migration * fix(core): ci --------- Co-authored-by: ottomao <ottomao@gmail.com>
1 parent c1762f8 commit 6d9705f

File tree

9 files changed

+73
-35
lines changed

9 files changed

+73
-35
lines changed

packages/cli/src/create-yaml-player.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,31 @@ export const launchServer = async (
4343
};
4444

4545
/**
46-
* Resolves the testId with proper priority handling.
47-
* Priority: CLI testId > YAML testId > fileName
46+
* Resolves reportFileName with proper priority handling.
47+
* Priority: YAML reportFileName > CLI testId (legacy) > YAML testId (legacy) > fileName
4848
*/
49-
function resolveTestId(
49+
function resolveReportFileName(
50+
yamlReportFileName: string | undefined,
5051
cliTestId: string | undefined,
5152
yamlTestId: string | undefined,
5253
fileName: string,
5354
): string {
54-
return cliTestId ?? yamlTestId ?? fileName;
55+
return yamlReportFileName ?? cliTestId ?? yamlTestId ?? fileName;
5556
}
5657

5758
/**
58-
* Builds agent options by merging YAML agent config with processed cache and testId.
59+
* Builds agent options by merging YAML agent config with processed cache and report name.
5960
* Handles the spread of agent options and ensures proper cache configuration.
6061
*/
6162
function buildAgentOptions(
6263
yamlAgent: MidsceneYamlScriptAgentOpt | undefined,
63-
preferenceTestId: string,
64+
reportFileName: string,
6465
fileName: string,
6566
): Partial<AgentOpt> {
6667
return {
6768
...(yamlAgent || {}),
6869
cache: processCacheConfig(yamlAgent?.cache, fileName),
69-
testId: preferenceTestId,
70+
reportFileName,
7071
};
7172
}
7273

@@ -92,8 +93,8 @@ export async function createYamlPlayer(
9293
const preference = {
9394
headed: options?.headed,
9495
keepWindow: options?.keepWindow,
95-
// Priority: CLI testId > YAML testId > fileName
96-
testId: resolveTestId(
96+
reportFileName: resolveReportFileName(
97+
clonedYamlScript.agent?.reportFileName,
9798
options?.testId,
9899
clonedYamlScript.agent?.testId,
99100
fileName,
@@ -194,7 +195,7 @@ export async function createYamlPlayer(
194195
...preference,
195196
...buildAgentOptions(
196197
clonedYamlScript.agent,
197-
preference.testId,
198+
preference.reportFileName,
198199
fileName,
199200
),
200201
},
@@ -226,7 +227,7 @@ export async function createYamlPlayer(
226227
...preference,
227228
...buildAgentOptions(
228229
clonedYamlScript.agent,
229-
preference.testId,
230+
preference.reportFileName,
230231
fileName,
231232
),
232233
},
@@ -262,7 +263,7 @@ export async function createYamlPlayer(
262263
closeConflictServer: true,
263264
...buildAgentOptions(
264265
clonedYamlScript.agent,
265-
preference.testId,
266+
preference.reportFileName,
266267
fileName,
267268
),
268269
});
@@ -295,7 +296,7 @@ export async function createYamlPlayer(
295296
...androidTarget, // Pass all Android config options
296297
...buildAgentOptions(
297298
clonedYamlScript.agent,
298-
preference.testId,
299+
preference.reportFileName,
299300
fileName,
300301
),
301302
});
@@ -320,7 +321,7 @@ export async function createYamlPlayer(
320321
...iosTarget, // Pass all iOS config options
321322
...buildAgentOptions(
322323
clonedYamlScript.agent,
323-
preference.testId,
324+
preference.reportFileName,
324325
fileName,
325326
),
326327
});
@@ -345,7 +346,7 @@ export async function createYamlPlayer(
345346
...computerTarget,
346347
...buildAgentOptions(
347348
clonedYamlScript.agent,
348-
preference.testId,
349+
preference.reportFileName,
349350
fileName,
350351
),
351352
});
@@ -405,7 +406,7 @@ export async function createYamlPlayer(
405406
device,
406407
buildAgentOptions(
407408
clonedYamlScript.agent,
408-
preference.testId,
409+
preference.reportFileName,
409410
fileName,
410411
),
411412
);

packages/cli/tests/unit-test/create-yaml-player.test.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -717,11 +717,11 @@ describe('create-yaml-player', () => {
717717
await setupFnCallback();
718718
}
719719

720-
// Verify that when agent config is undefined, testId is still set from fileName
720+
// Verify that when agent config is undefined, reportFileName is set from fileName
721721
// and aiActionContext is not present (undefined fields are not spread)
722722
const callArgs = getMockCallArg(vi.mocked(agentFromAdbDevice), 0, 1);
723723
expect(callArgs).toMatchObject({
724-
testId: 'script',
724+
reportFileName: 'script',
725725
deviceId: 'test-device',
726726
});
727727
expect(callArgs).not.toHaveProperty('aiActionContext');
@@ -754,11 +754,11 @@ describe('create-yaml-player', () => {
754754
await setupFnCallback();
755755
}
756756

757-
// Verify that when agent config is undefined, testId is still set from fileName
757+
// Verify that when agent config is undefined, reportFileName is set from fileName
758758
// and aiActionContext is not present (undefined fields are not spread)
759759
const callArgs = getMockCallArg(vi.mocked(agentFromWebDriverAgent), 0, 0);
760760
expect(callArgs).toMatchObject({
761-
testId: 'script',
761+
reportFileName: 'script',
762762
});
763763
expect(callArgs).not.toHaveProperty('aiActionContext');
764764
});
@@ -772,7 +772,6 @@ describe('create-yaml-player', () => {
772772
url: 'test.html',
773773
},
774774
agent: {
775-
testId: 'custom-test-id',
776775
groupName: 'Custom Group',
777776
groupDescription: 'Custom description',
778777
generateReport: true,
@@ -820,7 +819,6 @@ describe('create-yaml-player', () => {
820819
reportFileName: 'custom-report',
821820
replanningCycleLimit: 25,
822821
aiActionContext: 'Test context',
823-
testId: 'custom-test-id', // YAML testId is used
824822
}),
825823
undefined, // browser
826824
undefined, // page
@@ -951,7 +949,7 @@ describe('create-yaml-player', () => {
951949
);
952950
});
953951

954-
test('should prioritize CLI preference testId over YAML testId', async () => {
952+
test('should prioritize CLI legacy testId over YAML legacy testId for reportFileName', async () => {
955953
const mockScript: MidsceneYamlScript = {
956954
web: {
957955
serve: './test',
@@ -987,18 +985,18 @@ describe('create-yaml-player', () => {
987985
await setupFnCallback();
988986
}
989987

990-
// CLI testId should take priority
988+
// CLI legacy testId should take priority for reportFileName
991989
expect(puppeteerAgentForTarget).toHaveBeenCalledWith(
992990
expect.any(Object),
993991
expect.objectContaining({
994-
testId: 'cli-test-id',
992+
reportFileName: 'cli-test-id',
995993
}),
996994
undefined, // browser
997995
undefined, // page
998996
);
999997
});
1000998

1001-
test('should use YAML testId when no preference testId exists', async () => {
999+
test('should use YAML legacy testId as reportFileName when no explicit reportFileName exists', async () => {
10021000
const mockScript: MidsceneYamlScript = {
10031001
web: {
10041002
serve: './test',
@@ -1032,11 +1030,11 @@ describe('create-yaml-player', () => {
10321030
await setupFnCallback();
10331031
}
10341032

1035-
// When no explicit CLI testId is provided, YAML testId takes precedence over fileName
1033+
// When no explicit reportFileName/CLI value is provided, YAML legacy testId takes precedence over fileName
10361034
expect(puppeteerAgentForTarget).toHaveBeenCalledWith(
10371035
expect.any(Object),
10381036
expect.objectContaining({
1039-
testId: 'yaml-test-id',
1037+
reportFileName: 'yaml-test-id',
10401038
}),
10411039
undefined, // browser
10421040
undefined, // page
@@ -1182,7 +1180,7 @@ describe('create-yaml-player', () => {
11821180
expect(puppeteerAgentForTarget).toHaveBeenCalledWith(
11831181
expect.any(Object),
11841182
expect.objectContaining({
1185-
testId: 'cdp-test',
1183+
reportFileName: 'cdp-test',
11861184
groupName: 'CDP Tests',
11871185
}),
11881186
expect.any(Object),

packages/core/src/agent/agent.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,8 @@ export class Agent<
351351
this.dump = this.resetDump();
352352
this.reportFileName =
353353
opts?.reportFileName ||
354+
// Keep deprecated testId behavior for generated report names until it is
355+
// fully removed from the public API.
354356
getReportFileName(opts?.testId || this.interface.interfaceType || 'web');
355357

356358
this.reportGenerator = ReportGenerator.create(this.reportFileName!, {
@@ -1488,7 +1490,7 @@ export class Agent<
14881490
// Use the unified utils function to process cache configuration
14891491
const cacheConfig = processCacheConfig(
14901492
opts.cache,
1491-
opts.cacheId || opts.testId || 'default',
1493+
opts.cacheId || 'default',
14921494
);
14931495

14941496
if (!cacheConfig) {

packages/core/src/report-generator.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export class ReportGenerator implements IReportGenerator {
9595

9696
// In browser environment, file system is not available
9797
if (ifInBrowser) return nullReportGenerator;
98+
validateReportFileName(reportFileName);
9899

99100
if (opts.outputFormat === 'html-and-external-assets') {
100101
const outputDir = join(getMidsceneRunSubDir('report'), reportFileName);
@@ -293,3 +294,21 @@ export class ReportGenerator implements IReportGenerator {
293294
);
294295
}
295296
}
297+
298+
function validateReportFileName(reportFileName: string): void {
299+
if (!reportFileName?.trim()) {
300+
throw new Error('reportFileName must be a non-empty string');
301+
}
302+
303+
if (/[\\/]/.test(reportFileName)) {
304+
throw new Error(
305+
'reportFileName must not contain path separators (`/` or `\\\\`)',
306+
);
307+
}
308+
309+
if (/[:*?"<>|]/.test(reportFileName)) {
310+
throw new Error(
311+
'reportFileName contains illegal filename characters: : * ? " < > |',
312+
);
313+
}
314+
}

packages/core/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,7 @@ export type Cache =
10731073
| CacheConfig; // Object configuration (requires explicit id)
10741074

10751075
export interface AgentOpt {
1076+
// @deprecated Use `reportFileName` and `cache.id` instead.
10761077
testId?: string;
10771078
// @deprecated
10781079
cacheId?: string; // Keep backward compatibility, but marked as deprecated

packages/core/src/yaml.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ export interface MidsceneYamlTask {
7676
* fields like functions and complex objects. All fields are optional.
7777
*
7878
* @remarks
79-
* - testId priority: CLI parameter > YAML agent.testId > filename
79+
* - testId is deprecated; prefer reportFileName and cache.id
8080
* - These settings apply to all platforms (Web, Android, iOS, Generic Interface)
8181
* - modelConfig is configured through environment variables, not in YAML
8282
*
8383
* @example
8484
* ```yaml
8585
* agent:
86-
* testId: "checkout-test"
86+
* reportFileName: "checkout-report"
8787
* groupName: "E2E Test Suite"
8888
* generateReport: true
8989
* replanningCycleLimit: 30
@@ -94,7 +94,7 @@ export interface MidsceneYamlTask {
9494
*/
9595
export type MidsceneYamlScriptAgentOpt = Pick<
9696
AgentOpt,
97-
| 'testId'
97+
| 'testId' // deprecated, kept for backward compatibility
9898
| 'groupName'
9999
| 'groupDescription'
100100
| 'generateReport'

packages/core/tests/unit-test/report-generator.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,24 @@ describe('ReportGenerator — append-only model', () => {
619619
expect(reportPath).toContain('test-dir');
620620
expect(reportPath).toContain('index.html');
621621
});
622+
623+
it('should throw for reportFileName with path separators', () => {
624+
expect(() => ReportGenerator.create('../bad-name', {})).toThrow(
625+
'reportFileName must not contain path separators',
626+
);
627+
expect(() => ReportGenerator.create('bad/name', {})).toThrow(
628+
'reportFileName must not contain path separators',
629+
);
630+
});
631+
632+
it('should throw for reportFileName with illegal filename characters', () => {
633+
expect(() => ReportGenerator.create('bad:name', {})).toThrow(
634+
'reportFileName contains illegal filename characters',
635+
);
636+
expect(() => ReportGenerator.create('bad*name', {})).toThrow(
637+
'reportFileName contains illegal filename characters',
638+
);
639+
});
622640
});
623641

624642
describe('lazy loading — memory release behavior', () => {

packages/web-integration/src/playwright/ai-fixture.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export const PlaywrightAiFixture = (options?: {
9393
const cacheConfig = processTestCacheConfig(testInfo);
9494

9595
pageAgentMap[idForPage] = new PlaywrightAgent(page, {
96-
testId: `playwright-${testId}-${idForPage}`,
96+
reportFileName: `playwright-${testId}-${idForPage}`,
9797
forceSameTabNavigation,
9898
cache: cacheConfig,
9999
groupName: title,

packages/web-integration/src/puppeteer/agent-launcher.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,6 @@ export async function puppeteerAgentForTarget(
344344
} & Partial<
345345
Pick<
346346
AgentOpt,
347-
| 'testId'
348347
| 'groupName'
349348
| 'groupDescription'
350349
| 'generateReport'

0 commit comments

Comments
 (0)