@probolabs/playwright 1.5.0-rc.8 โ 1.5.0-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/cli.js +123 -60
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +124 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +26 -3
- package/dist/index.js +124 -61
- package/dist/index.js.map +1 -1
- package/dist/types/codegen-api.d.ts.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/test-suite-runner.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1315,6 +1315,16 @@ const highlighterCode = "(function (global, factory) {\n typeof exports === '
|
|
|
1315
1315
|
};
|
|
1316
1316
|
|
|
1317
1317
|
// --- Code generation utilities for Probo Labs Playwright scripts ---
|
|
1318
|
+
/**
|
|
1319
|
+
* Converts a test suite name to a Playwright tag (slugify + @ prefix).
|
|
1320
|
+
* Playwright tags must start with @.
|
|
1321
|
+
*/
|
|
1322
|
+
function slugifyTag(tag) {
|
|
1323
|
+
if (!tag || typeof tag !== 'string')
|
|
1324
|
+
return '@untagged';
|
|
1325
|
+
const slug = slugify(tag);
|
|
1326
|
+
return slug ? `@${slug}` : '@untagged';
|
|
1327
|
+
}
|
|
1318
1328
|
/**
|
|
1319
1329
|
* Extracts environment variable names from parameter table rows
|
|
1320
1330
|
* by parsing ${process.env.VAR_NAME} patterns from parameter values
|
|
@@ -1623,6 +1633,13 @@ const highlighterCode = "(function (global, factory) {\n typeof exports === '
|
|
|
1623
1633
|
}).join(',\n ')}
|
|
1624
1634
|
];
|
|
1625
1635
|
` : '';
|
|
1636
|
+
// Playwright tags for test suite filtering (e.g. npx playwright test --grep @sanity)
|
|
1637
|
+
const playwrightTags = options.testSuiteTags && options.testSuiteTags.length > 0
|
|
1638
|
+
? options.testSuiteTags.map(slugifyTag).filter((t) => t !== '@untagged')
|
|
1639
|
+
: [];
|
|
1640
|
+
const describeTagOption = playwrightTags.length > 0
|
|
1641
|
+
? `{ tag: [${playwrightTags.map((t) => `'${t}'`).join(', ')}] }, `
|
|
1642
|
+
: '';
|
|
1626
1643
|
const proboConstructor = hasAiInteractions ? `
|
|
1627
1644
|
const probo = new Probo({
|
|
1628
1645
|
scenarioName: '${options.scenarioName}',
|
|
@@ -1699,7 +1716,7 @@ ${hasSecrets ? `import { config } from 'dotenv';
|
|
|
1699
1716
|
}
|
|
1700
1717
|
});
|
|
1701
1718
|
|
|
1702
|
-
test.describe('${options.scenarioName}', () => {
|
|
1719
|
+
test.describe('${options.scenarioName}', ${describeTagOption}() => {
|
|
1703
1720
|
test.beforeEach(async ({ page }) => {
|
|
1704
1721
|
// set the ProboPlaywright instance to the current page
|
|
1705
1722
|
ppw.setPage(page);
|
|
@@ -1775,7 +1792,7 @@ ${hasSecrets ? `import { config } from 'dotenv';
|
|
|
1775
1792
|
* Generate package.json content for a Playwright test project
|
|
1776
1793
|
*/
|
|
1777
1794
|
function generatePackageJson(options) {
|
|
1778
|
-
const { name, hasSecrets = false, testScript = 'npx playwright test', playwrightTestVersion = DEFAULT_PLAYWRIGHT_TEST_VERSION } = options;
|
|
1795
|
+
const { name, hasSecrets = false, testScript = 'npx playwright test', playwrightTestVersion = DEFAULT_PLAYWRIGHT_TEST_VERSION, probolabsPlaywrightVersion = 'latest' } = options;
|
|
1779
1796
|
return `{
|
|
1780
1797
|
"name": "${name}",
|
|
1781
1798
|
"version": "1.0.0",
|
|
@@ -1786,7 +1803,7 @@ ${hasSecrets ? `import { config } from 'dotenv';
|
|
|
1786
1803
|
},
|
|
1787
1804
|
"dependencies": {
|
|
1788
1805
|
"@playwright/test": "${playwrightTestVersion}",
|
|
1789
|
-
"@probolabs/playwright": "
|
|
1806
|
+
"@probolabs/playwright": "${probolabsPlaywrightVersion}"${hasSecrets ? ',\n "dotenv": "latest"' : ''}
|
|
1790
1807
|
}
|
|
1791
1808
|
}`;
|
|
1792
1809
|
}
|
|
@@ -6130,12 +6147,12 @@ export default class ProboReporter implements Reporter {
|
|
|
6130
6147
|
return response.json();
|
|
6131
6148
|
}
|
|
6132
6149
|
/**
|
|
6133
|
-
* Fetch all scenarios for a project from the backend API
|
|
6150
|
+
* Fetch all scenarios for a project from the backend API (light endpoint includes test_suite_tags)
|
|
6134
6151
|
*/
|
|
6135
6152
|
static async fetchProjectScenarios(projectId, apiToken, apiUrl) {
|
|
6136
6153
|
const normalizedUrl = this.normalizeApiUrl(apiUrl);
|
|
6137
|
-
//
|
|
6138
|
-
const url = `${normalizedUrl}/scenarios/?project_id=${projectId}`;
|
|
6154
|
+
// Use api/scenarios/light/ to get test_suite_tags for Playwright tagging
|
|
6155
|
+
const url = `${normalizedUrl}/api/scenarios/light/?project_id=${projectId}`;
|
|
6139
6156
|
const response = await fetch(url, {
|
|
6140
6157
|
method: 'GET',
|
|
6141
6158
|
headers: {
|
|
@@ -6273,6 +6290,7 @@ export default class ProboReporter implements Reporter {
|
|
|
6273
6290
|
scenarioName: scenarioData.name,
|
|
6274
6291
|
interactions: interactions,
|
|
6275
6292
|
rows: rows,
|
|
6293
|
+
...((options === null || options === void 0 ? void 0 : options.testSuiteTags) && options.testSuiteTags.length > 0 ? { testSuiteTags: options.testSuiteTags } : {}),
|
|
6276
6294
|
};
|
|
6277
6295
|
return generateCode(codeOptions, settings, viewPort);
|
|
6278
6296
|
}
|
|
@@ -6332,14 +6350,15 @@ export default class ProboReporter implements Reporter {
|
|
|
6332
6350
|
if (scenarios.length === 0) {
|
|
6333
6351
|
throw new Error(`No scenarios found in project "${projectData.name}" (ID: ${projectId})`);
|
|
6334
6352
|
}
|
|
6335
|
-
// Generate code for each scenario
|
|
6353
|
+
// Generate code for each scenario (with test_suite_tags for Playwright tagging)
|
|
6336
6354
|
const scenarioResults = await Promise.all(scenarios.map(async (scenario) => {
|
|
6337
6355
|
try {
|
|
6338
|
-
const code = await this.generateCodeForScenario(scenario.id, apiToken, apiUrl, options);
|
|
6356
|
+
const code = await this.generateCodeForScenario(scenario.id, apiToken, apiUrl, { ...options, testSuiteTags: scenario.test_suite_tags });
|
|
6339
6357
|
return {
|
|
6340
6358
|
scenarioId: scenario.id,
|
|
6341
6359
|
scenarioName: scenario.name,
|
|
6342
6360
|
code: code,
|
|
6361
|
+
testSuiteTags: scenario.test_suite_tags,
|
|
6343
6362
|
};
|
|
6344
6363
|
}
|
|
6345
6364
|
catch (error) {
|
|
@@ -6366,15 +6385,13 @@ export default class ProboReporter implements Reporter {
|
|
|
6366
6385
|
}
|
|
6367
6386
|
}
|
|
6368
6387
|
/**
|
|
6369
|
-
* Gets the
|
|
6388
|
+
* Gets the project directory path (unified for test suite runs and export).
|
|
6389
|
+
* Path: ~/.probium/projects/{projectId}-{projectName-slugified}/
|
|
6370
6390
|
*/
|
|
6371
|
-
function
|
|
6372
|
-
const baseDir = path__namespace.join(os__namespace.homedir(), '.probium', '
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
return path__namespace.join(baseDir, `${testSuiteId}-${sanitizedName}`);
|
|
6376
|
-
}
|
|
6377
|
-
return path__namespace.join(baseDir, testSuiteId.toString());
|
|
6391
|
+
function getProjectDir(projectId, projectName) {
|
|
6392
|
+
const baseDir = path__namespace.join(os__namespace.homedir(), '.probium', 'projects');
|
|
6393
|
+
const sanitizedName = projectName ? slugify(projectName) : `project-${projectId}`;
|
|
6394
|
+
return path__namespace.join(baseDir, `${projectId}-${sanitizedName}`);
|
|
6378
6395
|
}
|
|
6379
6396
|
/**
|
|
6380
6397
|
* Count total tests by scanning spec files
|
|
@@ -6569,36 +6586,67 @@ export default class ProboReporter implements Reporter {
|
|
|
6569
6586
|
throw new Error(`Test suite "${testSuiteName}" not found in project "${projectName}"`);
|
|
6570
6587
|
}
|
|
6571
6588
|
/**
|
|
6572
|
-
*
|
|
6589
|
+
* Fetch test suite details (project id, project name, test suite name)
|
|
6590
|
+
*/
|
|
6591
|
+
static async fetchTestSuiteDetails(testSuiteId, apiToken, apiUrl) {
|
|
6592
|
+
var _a, _b, _c;
|
|
6593
|
+
const baseUrl = apiUrl.endsWith('/') ? apiUrl.slice(0, -1) : apiUrl;
|
|
6594
|
+
const response = await fetch$1(`${baseUrl}/test-suites/${testSuiteId}/`, {
|
|
6595
|
+
method: 'GET',
|
|
6596
|
+
headers: {
|
|
6597
|
+
'Authorization': `Token ${apiToken}`,
|
|
6598
|
+
'Content-Type': 'application/json',
|
|
6599
|
+
},
|
|
6600
|
+
});
|
|
6601
|
+
if (!response.ok) {
|
|
6602
|
+
const errorText = await response.text();
|
|
6603
|
+
throw new Error(`Failed to fetch test suite ${testSuiteId}: ${response.status} ${errorText}`);
|
|
6604
|
+
}
|
|
6605
|
+
const data = await response.json();
|
|
6606
|
+
const projectId = (_a = data.project) !== null && _a !== void 0 ? _a : data.project_id;
|
|
6607
|
+
const projectName = (_b = data.project_name) !== null && _b !== void 0 ? _b : `project-${projectId}`;
|
|
6608
|
+
const testSuiteName = (_c = data.name) !== null && _c !== void 0 ? _c : `suite-${testSuiteId}`;
|
|
6609
|
+
if (!projectId) {
|
|
6610
|
+
throw new Error(`Test suite ${testSuiteId} has no project`);
|
|
6611
|
+
}
|
|
6612
|
+
return { projectId, projectName, testSuiteName };
|
|
6613
|
+
}
|
|
6614
|
+
/**
|
|
6615
|
+
* Generate all files for a test suite (writes to project-level directory)
|
|
6573
6616
|
*/
|
|
6574
6617
|
static async generateTestSuiteFiles(testSuiteId, apiToken, apiUrl, outputDir, testSuiteName, includeReporter = true, runId) {
|
|
6575
|
-
const
|
|
6576
|
-
|
|
6618
|
+
const { projectId, projectName, testSuiteName: resolvedSuiteName } = await this.fetchTestSuiteDetails(testSuiteId, apiToken, apiUrl);
|
|
6619
|
+
const projectDir = outputDir || getProjectDir(projectId, projectName);
|
|
6620
|
+
await this.generateTestSuiteOnlyFiles(testSuiteId, apiToken, apiUrl, projectDir, testSuiteName !== null && testSuiteName !== void 0 ? testSuiteName : resolvedSuiteName, includeReporter, runId);
|
|
6621
|
+
}
|
|
6622
|
+
/**
|
|
6623
|
+
* Generate files only for the scenarios that participate in the test suite.
|
|
6624
|
+
* Used by the recorder app test-suite runner so only suite specs are written; then we run npx playwright test normally.
|
|
6625
|
+
*/
|
|
6626
|
+
static async generateTestSuiteOnlyFiles(testSuiteId, apiToken, apiUrl, outputDir, testSuiteName, includeReporter = true, runId) {
|
|
6627
|
+
var _a;
|
|
6628
|
+
const { projectId } = await this.fetchTestSuiteDetails(testSuiteId, apiToken, apiUrl);
|
|
6577
6629
|
const codeGenResult = await ProboCodeGenerator.generateCodeForTestSuite(testSuiteId, apiToken, apiUrl);
|
|
6578
6630
|
if (codeGenResult.scenarios.length === 0) {
|
|
6579
|
-
throw new Error(`No scenarios found in test suite ${testSuiteId}. Cannot generate test files.`);
|
|
6631
|
+
throw new Error(`No scenarios found in test suite "${codeGenResult.testSuiteName}" (ID: ${testSuiteId}). Cannot generate test files.`);
|
|
6580
6632
|
}
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
if (fs__namespace.existsSync(testSuiteDir)) {
|
|
6633
|
+
const projectDir = outputDir;
|
|
6634
|
+
if (fs__namespace.existsSync(projectDir)) {
|
|
6584
6635
|
try {
|
|
6585
|
-
const nodeModulesPath = path__namespace.join(
|
|
6636
|
+
const nodeModulesPath = path__namespace.join(projectDir, 'node_modules');
|
|
6586
6637
|
const hasNodeModules = fs__namespace.existsSync(nodeModulesPath);
|
|
6587
|
-
// Temporarily move node_modules out of the way if it exists
|
|
6588
6638
|
let tempNodeModulesPath = null;
|
|
6589
6639
|
if (hasNodeModules) {
|
|
6590
|
-
tempNodeModulesPath = path__namespace.join(
|
|
6591
|
-
// Remove temp directory if it exists from a previous failed run
|
|
6640
|
+
tempNodeModulesPath = path__namespace.join(projectDir, '..', `node_modules.temp.${projectId}`);
|
|
6592
6641
|
if (fs__namespace.existsSync(tempNodeModulesPath)) {
|
|
6593
6642
|
fs__namespace.rmSync(tempNodeModulesPath, { recursive: true, force: true });
|
|
6594
6643
|
}
|
|
6595
6644
|
fs__namespace.renameSync(nodeModulesPath, tempNodeModulesPath);
|
|
6596
6645
|
console.log(`๐ฆ Preserved node_modules temporarily`);
|
|
6597
6646
|
}
|
|
6598
|
-
|
|
6599
|
-
const entries = fs__namespace.readdirSync(testSuiteDir, { withFileTypes: true });
|
|
6647
|
+
const entries = fs__namespace.readdirSync(projectDir, { withFileTypes: true });
|
|
6600
6648
|
for (const entry of entries) {
|
|
6601
|
-
const entryPath = path__namespace.join(
|
|
6649
|
+
const entryPath = path__namespace.join(projectDir, entry.name);
|
|
6602
6650
|
try {
|
|
6603
6651
|
if (entry.isDirectory()) {
|
|
6604
6652
|
fs__namespace.rmSync(entryPath, { recursive: true, force: true });
|
|
@@ -6611,36 +6659,33 @@ export default class ProboReporter implements Reporter {
|
|
|
6611
6659
|
console.warn(`โ ๏ธ Failed to delete ${entry.name}:`, error);
|
|
6612
6660
|
}
|
|
6613
6661
|
}
|
|
6614
|
-
console.log(`๐๏ธ Cleaned
|
|
6615
|
-
// Move node_modules back
|
|
6662
|
+
console.log(`๐๏ธ Cleaned project directory (preserved node_modules)`);
|
|
6616
6663
|
if (hasNodeModules && tempNodeModulesPath) {
|
|
6617
6664
|
fs__namespace.renameSync(tempNodeModulesPath, nodeModulesPath);
|
|
6618
6665
|
console.log(`๐ฆ Restored node_modules`);
|
|
6619
6666
|
}
|
|
6620
6667
|
}
|
|
6621
6668
|
catch (error) {
|
|
6622
|
-
console.warn(`โ ๏ธ Failed to clean
|
|
6623
|
-
// Continue anyway - we'll overwrite files as needed
|
|
6669
|
+
console.warn(`โ ๏ธ Failed to clean project directory ${projectDir}:`, error);
|
|
6624
6670
|
}
|
|
6625
6671
|
}
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
const testsDir = path__namespace.join(testSuiteDir, 'tests');
|
|
6672
|
+
ensureDirectoryExists(projectDir);
|
|
6673
|
+
const testsDir = path__namespace.join(projectDir, 'tests');
|
|
6629
6674
|
ensureDirectoryExists(testsDir);
|
|
6630
|
-
|
|
6675
|
+
const seenSlugs = new Map();
|
|
6631
6676
|
for (const scenario of codeGenResult.scenarios) {
|
|
6632
|
-
const
|
|
6677
|
+
const slug = slugify(scenario.scenarioName);
|
|
6678
|
+
const count = (_a = seenSlugs.get(slug)) !== null && _a !== void 0 ? _a : 0;
|
|
6679
|
+
seenSlugs.set(slug, count + 1);
|
|
6680
|
+
const fileName = count > 0 ? `${slug}-${scenario.scenarioId}.spec.ts` : `${slug}.spec.ts`;
|
|
6633
6681
|
const filePath = path__namespace.join(testsDir, fileName);
|
|
6634
6682
|
fs__namespace.writeFileSync(filePath, scenario.code, 'utf-8');
|
|
6635
6683
|
console.log(`โ
Generated test file: ${filePath}`);
|
|
6636
6684
|
}
|
|
6637
|
-
|
|
6638
|
-
await this.
|
|
6639
|
-
// Generate playwright.config.ts with runId if available
|
|
6640
|
-
await this.generatePlaywrightConfig(testSuiteDir, includeReporter, runId);
|
|
6641
|
-
// Generate custom reporter file for live progress streaming (only if requested)
|
|
6685
|
+
await this.generatePackageJson(projectDir, codeGenResult);
|
|
6686
|
+
await this.generatePlaywrightConfig(projectDir, includeReporter, runId);
|
|
6642
6687
|
if (includeReporter) {
|
|
6643
|
-
await this.generateProboReporter(
|
|
6688
|
+
await this.generateProboReporter(projectDir);
|
|
6644
6689
|
}
|
|
6645
6690
|
}
|
|
6646
6691
|
/**
|
|
@@ -6684,7 +6729,8 @@ export default class ProboReporter implements Reporter {
|
|
|
6684
6729
|
const packageJsonContent = generatePackageJson({
|
|
6685
6730
|
name: sanitizedName,
|
|
6686
6731
|
hasSecrets: hasSecrets,
|
|
6687
|
-
testScript: 'npx playwright test'
|
|
6732
|
+
testScript: 'npx playwright test',
|
|
6733
|
+
probolabsPlaywrightVersion: process.env.PROBO_PLAYWRIGHT_VERSION || 'latest',
|
|
6688
6734
|
});
|
|
6689
6735
|
const filePath = path__namespace.join(outputDir, 'package.json');
|
|
6690
6736
|
fs__namespace.writeFileSync(filePath, packageJsonContent, 'utf-8');
|
|
@@ -6714,7 +6760,8 @@ export default class ProboReporter implements Reporter {
|
|
|
6714
6760
|
*/
|
|
6715
6761
|
static async runTestSuite(testSuiteId, apiToken, apiUrl, testSuiteName, runId, options = {}) {
|
|
6716
6762
|
const { outputDir, includeReporter = true, playwrightArgs = [], skipRunCreation = false, onStatusUpdate, onStdout, onStderr, onReporterEvent, } = options;
|
|
6717
|
-
const
|
|
6763
|
+
const details = await this.fetchTestSuiteDetails(testSuiteId, apiToken, apiUrl);
|
|
6764
|
+
const projectDir = outputDir || getProjectDir(details.projectId, details.projectName);
|
|
6718
6765
|
let currentRunId = runId;
|
|
6719
6766
|
try {
|
|
6720
6767
|
// Create run record if not provided and not skipping run creation
|
|
@@ -6742,9 +6789,9 @@ export default class ProboReporter implements Reporter {
|
|
|
6742
6789
|
}
|
|
6743
6790
|
// Generate all files (with runId if available for run-specific report directories)
|
|
6744
6791
|
console.log(`๐ Generating test suite files for test suite ${testSuiteId}...`);
|
|
6745
|
-
await this.generateTestSuiteFiles(testSuiteId, apiToken, apiUrl,
|
|
6792
|
+
await this.generateTestSuiteFiles(testSuiteId, apiToken, apiUrl, projectDir, testSuiteName, includeReporter, currentRunId);
|
|
6746
6793
|
// Count total tests
|
|
6747
|
-
const testsTotal = countTotalTests(
|
|
6794
|
+
const testsTotal = countTotalTests(projectDir);
|
|
6748
6795
|
if (currentRunId && testsTotal > 0) {
|
|
6749
6796
|
await updateRunStatus(currentRunId, testSuiteId, { tests_total: testsTotal }, apiToken, apiUrl);
|
|
6750
6797
|
if (onStatusUpdate) {
|
|
@@ -6752,10 +6799,10 @@ export default class ProboReporter implements Reporter {
|
|
|
6752
6799
|
}
|
|
6753
6800
|
}
|
|
6754
6801
|
// Install dependencies
|
|
6755
|
-
console.log(`๐ฆ Installing dependencies in ${
|
|
6802
|
+
console.log(`๐ฆ Installing dependencies in ${projectDir}...`);
|
|
6756
6803
|
try {
|
|
6757
6804
|
const npmExecutable = findNpmExecutable();
|
|
6758
|
-
const { stdout: installStdout, stderr: installStderr } = await execAsync(`${npmExecutable} install`, { cwd:
|
|
6805
|
+
const { stdout: installStdout, stderr: installStderr } = await execAsync(`${npmExecutable} install`, { cwd: projectDir, timeout: 300000 } // 5 minute timeout for install
|
|
6759
6806
|
);
|
|
6760
6807
|
console.log('โ
Dependencies installed successfully');
|
|
6761
6808
|
if (installStdout)
|
|
@@ -6795,7 +6842,7 @@ export default class ProboReporter implements Reporter {
|
|
|
6795
6842
|
console.log(`๐ Installing Playwright Chromium browser...`);
|
|
6796
6843
|
try {
|
|
6797
6844
|
const npxExecutable = findNpxExecutable();
|
|
6798
|
-
const { stdout: browserStdout, stderr: browserStderr } = await execAsync(`${npxExecutable} playwright install chromium`, { cwd:
|
|
6845
|
+
const { stdout: browserStdout, stderr: browserStderr } = await execAsync(`${npxExecutable} playwright install chromium`, { cwd: projectDir, timeout: 300000 } // 5 minute timeout for browser install
|
|
6799
6846
|
);
|
|
6800
6847
|
console.log('โ
Playwright Chromium browser installed successfully');
|
|
6801
6848
|
if (browserStdout)
|
|
@@ -6809,7 +6856,7 @@ export default class ProboReporter implements Reporter {
|
|
|
6809
6856
|
// or the error will be caught when trying to run tests
|
|
6810
6857
|
}
|
|
6811
6858
|
// Run Playwright tests with streaming output
|
|
6812
|
-
console.log(`๐ Running Playwright tests in ${
|
|
6859
|
+
console.log(`๐ Running Playwright tests in ${projectDir}...`);
|
|
6813
6860
|
if (playwrightArgs.length > 0) {
|
|
6814
6861
|
console.log(`๐งช Playwright extra args: ${playwrightArgs.join(' ')}`);
|
|
6815
6862
|
}
|
|
@@ -6831,7 +6878,7 @@ export default class ProboReporter implements Reporter {
|
|
|
6831
6878
|
// Use spawn for streaming output
|
|
6832
6879
|
const npxExecutable = findNpxExecutable();
|
|
6833
6880
|
const testProcess = child_process.spawn(npxExecutable, ['playwright', 'test', ...playwrightArgs], {
|
|
6834
|
-
cwd:
|
|
6881
|
+
cwd: projectDir,
|
|
6835
6882
|
shell: true,
|
|
6836
6883
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
6837
6884
|
env: {
|
|
@@ -7017,8 +7064,8 @@ export default class ProboReporter implements Reporter {
|
|
|
7017
7064
|
console.log(success ? 'โ
Tests completed successfully' : (isTestFailure ? 'โ ๏ธ Tests completed with failures' : 'โ Test execution failed'));
|
|
7018
7065
|
// Parse final statistics from metadata/stdout as fallback
|
|
7019
7066
|
let parsedStats = null;
|
|
7020
|
-
if (fs__namespace.existsSync(path__namespace.join(
|
|
7021
|
-
parsedStats = parseStatisticsFromMetadata(
|
|
7067
|
+
if (fs__namespace.existsSync(path__namespace.join(projectDir, 'playwright-report'))) {
|
|
7068
|
+
parsedStats = parseStatisticsFromMetadata(projectDir);
|
|
7022
7069
|
}
|
|
7023
7070
|
if (!parsedStats) {
|
|
7024
7071
|
parsedStats = parsePlaywrightStatistics(stdout);
|
|
@@ -7220,7 +7267,8 @@ export default class ProboReporter implements Reporter {
|
|
|
7220
7267
|
* Generate all files for a project (all scenarios)
|
|
7221
7268
|
*/
|
|
7222
7269
|
static async generateProjectFiles(projectId, apiToken, apiUrl, outputDir, projectName, includeReporter = true) {
|
|
7223
|
-
|
|
7270
|
+
var _a;
|
|
7271
|
+
const projectDir = outputDir || getProjectDir(projectId, projectName || `project-${projectId}`);
|
|
7224
7272
|
// Generate code for all scenarios
|
|
7225
7273
|
const codeGenResult = await ProboCodeGenerator.generateCodeForProject(projectId, apiToken, apiUrl);
|
|
7226
7274
|
if (codeGenResult.scenarios.length === 0) {
|
|
@@ -7274,9 +7322,12 @@ export default class ProboReporter implements Reporter {
|
|
|
7274
7322
|
ensureDirectoryExists(projectDir);
|
|
7275
7323
|
const testsDir = path__namespace.join(projectDir, 'tests');
|
|
7276
7324
|
ensureDirectoryExists(testsDir);
|
|
7277
|
-
|
|
7325
|
+
const seenSlugs = new Map();
|
|
7278
7326
|
for (const scenario of codeGenResult.scenarios) {
|
|
7279
|
-
const
|
|
7327
|
+
const slug = slugify(scenario.scenarioName);
|
|
7328
|
+
const count = (_a = seenSlugs.get(slug)) !== null && _a !== void 0 ? _a : 0;
|
|
7329
|
+
seenSlugs.set(slug, count + 1);
|
|
7330
|
+
const fileName = count > 0 ? `${slug}-${scenario.scenarioId}.spec.ts` : `${slug}.spec.ts`;
|
|
7280
7331
|
const filePath = path__namespace.join(testsDir, fileName);
|
|
7281
7332
|
fs__namespace.writeFileSync(filePath, scenario.code, 'utf-8');
|
|
7282
7333
|
console.log(`โ
Generated test file: ${filePath}`);
|
|
@@ -7290,6 +7341,18 @@ export default class ProboReporter implements Reporter {
|
|
|
7290
7341
|
await this.generateProboReporter(projectDir);
|
|
7291
7342
|
}
|
|
7292
7343
|
}
|
|
7344
|
+
/**
|
|
7345
|
+
* Export project code to a directory.
|
|
7346
|
+
* If outputDir is provided, creates a subfolder {outputDir}/{projectId}-{projectNameSlug}/ and writes there.
|
|
7347
|
+
* Otherwise writes to the default project dir (~/.probium/projects/...).
|
|
7348
|
+
*/
|
|
7349
|
+
static async exportProjectCode(projectId, apiToken, apiUrl, outputDir, projectName, includeReporter = false) {
|
|
7350
|
+
const targetDir = outputDir
|
|
7351
|
+
? path__namespace.join(outputDir, `${projectId}-${slugify(projectName || `project-${projectId}`)}`)
|
|
7352
|
+
: getProjectDir(projectId, projectName);
|
|
7353
|
+
await this.generateProjectFiles(projectId, apiToken, apiUrl, targetDir, projectName, includeReporter);
|
|
7354
|
+
return targetDir;
|
|
7355
|
+
}
|
|
7293
7356
|
/**
|
|
7294
7357
|
* Run all scenarios in a project
|
|
7295
7358
|
* Generates files, installs dependencies, and executes tests
|
|
@@ -7298,7 +7361,7 @@ export default class ProboReporter implements Reporter {
|
|
|
7298
7361
|
static async runProjectScenarios(projectId, apiToken, apiUrl, projectName, options = {}) {
|
|
7299
7362
|
const { outputDir, includeReporter = false, // CLI mode - no custom reporter
|
|
7300
7363
|
playwrightArgs = [], onStdout, onStderr, } = options;
|
|
7301
|
-
const projectDir = outputDir ||
|
|
7364
|
+
const projectDir = outputDir || getProjectDir(projectId, projectName || `project-${projectId}`);
|
|
7302
7365
|
try {
|
|
7303
7366
|
// Generate all files
|
|
7304
7367
|
console.log(`๐ Generating test files for project ${projectId}...`);
|
|
@@ -7890,6 +7953,7 @@ export default class ProboReporter implements Reporter {
|
|
|
7890
7953
|
exports.extractRequiredEnvVars = extractRequiredEnvVars;
|
|
7891
7954
|
exports.findClosestVisibleElement = findClosestVisibleElement;
|
|
7892
7955
|
exports.generateCode = generateCode;
|
|
7956
|
+
exports.getProjectDir = getProjectDir;
|
|
7893
7957
|
exports.getRequiredEnvVars = getRequiredEnvVars;
|
|
7894
7958
|
exports.interpolateWithParams = interpolateWithParams;
|
|
7895
7959
|
|